/** * First 4 bytes of sha512(payload) */ private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException { byte[] d = sha512(bytes); return new byte[]{d[0], d[1], d[2], d[3]}; }
private static boolean testChecksum(byte[] checksum, byte[] payload) { byte[] payloadChecksum = Security.sha512(payload); for (int i = 0; i < checksum.length; i++) { if (checksum[i] != payloadChecksum[i]) { return false; } } return true; }
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) { return ripemd160(sha512(publicSigningKey, publicEncryptionKey)); }
private static byte[] getInitialHash(ObjectMessage object) throws IOException { return Security.sha512(object.getPayloadBytesWithoutNonce()); }
public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { try { // FIXME: this is most definitely wrong this.privateSigningKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); this.privateEncryptionKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); this.pubkey = Security.createPubkey(version, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } }
BitmessageAddress(long version, long stream, byte[] ripe) { try { this.version = version; this.stream = stream; this.ripe = ripe; ByteArrayOutputStream os = new ByteArrayOutputStream(); Encode.varInt(version, os); Encode.varInt(stream, os); if (version < 4) { byte[] checksum = Security.sha512(os.toByteArray(), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { // for tag and decryption key, the checksum has to be created with 0x00 padding byte[] checksum = Security.doubleSha512(os.toByteArray(), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } // but for the address and its checksum they need to be stripped int offset = Bytes.numberOfLeadingZeros(ripe); os.write(ripe, offset, ripe.length - offset); byte[] checksum = Security.doubleSha512(os.toByteArray()); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } }
public byte[] getRipe() { return ripemd160(sha512(getSigningKey(), getEncryptionKey())); }
public BitmessageAddress(String address) { try { this.address = address; byte[] bytes = Base58.decode(address.substring(3)); ByteArrayInputStream in = new ByteArrayInputStream(bytes); AccessCounter counter = new AccessCounter(); this.version = varInt(in, counter); this.stream = varInt(in, counter); this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20); // test checksum byte[] checksum = Security.doubleSha512(bytes, bytes.length - 4); byte[] expectedChecksum = bytes(in, 4); for (int i = 0; i < 4; i++) { if (expectedChecksum[i] != checksum[i]) throw new IllegalArgumentException("Checksum of address failed"); } if (version < 4) { checksum = Security.sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { checksum = Security.doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } } catch (IOException e) { throw new RuntimeException(e); } }
/** * @param privateKey a private key, typically should be 32 bytes long * @return an InputStream yielding the decrypted data * @throws DecryptionFailedException if the payload can't be decrypted using this private key * @see <a href='https://bitmessage.org/wiki/Encryption#Decryption'>https://bitmessage.org/wiki/Encryption#Decryption</a> */ public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException { // 1. The private key used to decrypt is called k. BigInteger k = Security.keyToBigInt(privateKey); // 2. Do an EC point multiply with private key k and public key R. This gives you public key P. ECPoint P = R.multiply(k).normalize(); // 3. Use the X component of public key P and calculate the SHA512 hash H. byte[] H = Security.sha512(P.getXCoord().getEncoded()); // 4. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 5. Calculate MAC' with HMACSHA256, using key_m as salt and IV + R + cipher text as data. // 6. Compare MAC with MAC'. If not equal, decryption will fail. if (!Arrays.equals(mac, calculateMac(key_m))) { throw new DecryptionFailedException(); } // 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key // and the cipher text as payload. The output is the padded input text. return new ByteArrayInputStream(crypt(false, encrypted, key_e)); }
public CryptoBox(Streamable data, ECPoint K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. // 2. Generate 16 random bytes using a secure random number generator. Call them IV. initializationVector = Security.randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. byte[] r = Security.randomBytes(PRIVATE_KEY_SIZE); R = Security.createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize(); byte[] X = P.getXCoord().getEncoded(); // 5. Use the X component of public key P and calculate the SHA512 hash H. byte[] H = Security.sha512(X); // 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. encrypted = crypt(true, Encode.bytes(data), key_e); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. mac = calculateMac(key_m); // The resulting data is: IV + R + cipher text + MAC }