/// <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(); } }