/// <summary> /// Calculates the whirlpool digest of the specified buffer. /// </summary> /// <param name="buf"> The buffer. </param> /// <returns> The 64-byte whirlpool digest. </returns> public static byte[] GetWhirlpoolDigest(this ByteBuffer buf) { var bytes = new byte[buf.limit()]; buf.get(bytes); return(Whirlpool.Crypt(bytes, 0, bytes.Length)); }
/// <summary> /// Decodes the <seealso cref="ChecksumTable" /> in the specified /// <seealso cref="ByteBuffer" /> and decrypts the final whirlpool hash. /// </summary> /// <param name="buffer"> The <seealso cref="ByteBuffer" /> containing the table. </param> /// <param name="whirlpool"> If whirlpool digests should be read. </param> /// <param name="modulus"> The modulus. </param> /// <param name="publicKey"> The public key. </param> /// <returns> The decoded <seealso cref="ChecksumTable" />. </returns> /// <exception cref="IOException"> if an I/O error occurs. </exception> public static ChecksumTable Decode(ByteBuffer buffer, bool whirlpool, BigInteger modulus, BigInteger publicKey) { /* find out how many entries there are and allocate a new table */ var size = whirlpool ? (buffer.limit() / 8) : (buffer.get() & 0xFF); var table = new ChecksumTable(size); /* calculate the whirlpool digest we expect to have at the end */ byte[] masterDigest = null; if (whirlpool) { var temp = new byte[size * 72 + 1]; buffer.position(0); buffer.get(temp); masterDigest = Whirlpool.Crypt(temp, 0, temp.Length); } /* read the entries */ buffer.position(1); for (var i = 0; i < size; i++) { var crc = buffer.getInt(); var version = buffer.getInt(); var digest = new byte[64]; if (whirlpool) { buffer.get(digest); } table.entries[i] = new Entry(crc, version, digest); } /* read the trailing digest and check if it matches up */ if (whirlpool) { var bytes = new byte[buffer.remaining()]; buffer.get(bytes); var temp = ByteBuffer.wrap(bytes); if (modulus != null && publicKey != null) { temp = Rsa.Crypt(buffer, modulus, publicKey); } if (temp.limit() != 66) { throw new IOException("Decrypted data is not 66 bytes long"); } for (var i = 0; i < 64; i++) { if (temp.get(i + 1) != masterDigest[i]) { throw new IOException("Whirlpool digest mismatch"); } } } /* if it looks good return the table */ return(table); }
/// <summary> /// Encodes this <seealso cref="ChecksumTable" /> and encrypts the final whirlpool hash. /// </summary> /// <param name="whirlpool"> If whirlpool digests should be encoded. </param> /// <param name="modulus"> The modulus. </param> /// <param name="privateKey"> The private key. </param> /// <returns> The encoded <seealso cref="ByteBuffer" />. </returns> /// <exception cref="IOException"> if an I/O error occurs. </exception> public virtual ByteBuffer Encode(bool whirlpool, BigInteger modulus, BigInteger privateKey) { var bout = new ByteArrayOutputStream(); var os = new DataOutputStream(bout); try { /* as the new whirlpool format is more complicated we must write the number of entries */ if (whirlpool) { os.write(entries.Length); } /* encode the individual entries */ for (var i = 0; i < entries.Length; i++) { var entry = entries[i]; os.writeInt(entry.GetCrc()); os.writeInt(entry.GetVersion()); if (whirlpool) { os.write(entry.GetWhirlpool()); } } /* compute (and encrypt) the digest of the whole table */ byte[] bytes; if (whirlpool) { bytes = bout.toByteArray(); var temp = ByteBuffer.allocate(66); temp.put(0); temp.put(Whirlpool.Crypt(bytes, 5, bytes.Length - 5)); temp.put(0); temp.flip(); if (modulus != null && privateKey != null) { temp = Rsa.Crypt(temp, modulus, privateKey); } bytes = new byte[temp.limit()]; temp.get(bytes); os.write(bytes); } bytes = bout.toByteArray(); return(ByteBuffer.wrap(bytes)); } finally { os.close(); } }
/// <summary> /// Writes a file to the cache and updates the <seealso cref="ReferenceTable" /> that /// it is associated with. /// </summary> /// <param name="type"> The type of file. </param> /// <param name="file"> The file id. </param> /// <param name="container"> The <seealso cref="Container" /> to write. </param> /// <exception cref="IOException"> if an I/O error occurs. </exception> public void Write(int type, int file, Container container) { /* we don't want people reading/manipulating these manually */ if (type == 255) { throw new IOException("Reference tables can only be modified with the low level FileStore API!"); } /* increment the container's version */ container.SetVersion(container.GetVersion() + 1); /* decode the reference table for this index */ var tableContainer = Container.Decode(store.Read(255, type)); var table = ReferenceTable.Decode(tableContainer.GetData()); /* grab the bytes we need for the checksum */ var buffer = container.Encode(); var bytes = new byte[buffer.limit() - 2]; // last two bytes are the version and shouldn't be included buffer.mark(); try { buffer.position(0); buffer.get(bytes, 0, bytes.Length); } finally { buffer.reset(); } /* calculate the new CRC checksum */ var crc = new CRC32(); crc.update(bytes, 0, bytes.Length); /* update the version and checksum for this file */ var entry = table.GetEntry(file); if (entry == null) { /* create a new entry for the file */ entry = new ReferenceTable.Entry(); table.PutEntry(file, entry); } entry.SetVersion(container.GetVersion()); entry.SetCrc((int)crc.getValue()); /* calculate and update the whirlpool digest if we need to */ if ((table.GetFlags() & ReferenceTable.FLAG_WHIRLPOOL) != 0) { var whirlpool = Whirlpool.Crypt(bytes, 0, bytes.Length); entry.SetWhirlpool(whirlpool); } /* update the reference table version */ table.SetVersion(table.GetVersion() + 1); /* save the reference table */ tableContainer = new Container(tableContainer.GetType(), table.Encode()); store.Write(255, type, tableContainer.Encode()); /* save the file itself */ store.Write(type, file, buffer); }