/** * Check that the packed anonymousId matches current key. Use {@link #createAnonymousId()} to get a random anonymous * id for this key. * * @param packedId * @return true if it matches. * @throws IOException */ public boolean matchAnonymousId(@NonNull byte[] packedId) throws IOException { assert (packedId.length == 64); HMAC hmac = new HMAC(fingerprint()); hmac.update(packedId, 0, 32); byte[] idDigest = Arrays.copyOfRange(packedId, 32, 64); return Arrays.equals(hmac.digest(), idDigest); }
private void end() throws IOException { byte[] readHmac = ring.readAll(); if (readHmac.length != hmac.getLength()) throw new IOException("stream corrupted: bad hmac record size:" + readHmac.length); if (!Arrays.equals(readHmac, hmac.digest())) { throw new AuthenticationFailed("HMAC authentication failed, data corrupted"); } } }
@Test public void digest() throws Exception { String ok = "la3Xl78Z3ktK2JLoDpPKthhqVilUX6+e6a0WultI9f8="; byte[] key = "1234567890abcdef1234567890abcdef".getBytes(); byte[] data = "a quick brown for his done something disgusting".getBytes(); HMAC hmac = new HMAC(key, Sha256.class); byte[] digest = hmac.digest(data); assertArrayEquals(Base64.decodeLines(ok), digest); // System.out.println(Base64.encodeString(digest)); }
EtaDecryptingStream(InputStream inputStream) throws IOException, EncryptionError { this.inputStream = inputStream; byte[] IV = new byte[getCipher().getBlockSize()]; inputStream.read(IV); transformer = new CTRTransformer(getCipher(), IV); hmac = new HMAC(key); // We should have always block bytes in the buffer to finish: ring = new ByteRingBuffer(hmac.getLength() + 8); for (int i = 0; i < hmac.getLength(); i++) ring.put(inputStream.read()); }
private Digest hashInstance() { return new HMAC(passwordBytes, hashClass); }
/** * Finishes encryption and writes down HMAC record. Futhher writing will cause {@link * EOFException}. Does not close the underlying output stream! * * @throws IOException */ public void end() throws IOException { if (done) return; done = true; outputStream.write(hmac.digest()); }
@Override protected byte[] _digest() { Digest d = hashInstance(); d.update(oKeyPad); d.update(hash.digest()); return d.digest(); }
@Override public void write(int plain) throws IOException { if (done) throw new EOFException("can't write past the end()"); try { int encrypted = transformer == null ? plain : transformer.transformByte(plain); hmac.update(encrypted); outputStream.write(encrypted); } catch (EncryptionError encryptionError) { throw new IOException("failed to encrypt", encryptionError); } } }
EtaEncryptingStream(OutputStream outputStream, boolean encrypt) throws IOException, EncryptionError { int blockSize = 64; // for SHA256 at least this.outputStream = outputStream; hmac = new HMAC(key); if (encrypt) { transformer = new CTRTransformer(getCipher(), null); outputStream.write(transformer.getIV()); } }
/** * Create new HMAC hash. * * @param key * secret key * @param hashClass * class of the hash to use to sign the message. SHA256+ is recommended. */ public HMAC(byte[] key, Class<? extends Digest> hashClass) { this.hashClass = hashClass; this.hash = hashInstance(); this.blockSize = hash.getChunkSize(); byte[] keyBlock; if (key.length > blockSize) { byte[] kd = new Sha256().digest(key); keyBlock = Arrays.copyOf(kd, blockSize); } else keyBlock = Arrays.copyOf(key, blockSize); oKeyPad = xor(keyBlock, 0x5c); byte[] iKeyPad = xor(keyBlock, 0x36); hash.update(iKeyPad); }
@Override public int read() throws IOException { int nextByte = inputStream.read(); if (nextByte < 0) { readingFinished = true; end(); return -1; } else { ring.put(nextByte); try { int encrypted = ring.get(); hmac.update(encrypted); return transformer.transformByte(encrypted); } catch (EncryptionError encryptionError) { throw new IOException("failed to decrypt", encryptionError); } } }
/** * Create a random (e.g. every call a different) sequence of bytes that identidy this key. There can almost infinite * number if anonynous ids for e key (more than 1.0E77), so it is really anonymous way to identify some key. The * anonymousId for public and private keys are the same. * <p> * Anonymous ID size is 64 bytes: first are 32 random bytes, last are HMAC(key, sha256) of these random bytes. * <p> * The most important thing about anonymous ids is that every time this call generates new id for the same key, * providing anonymous but exact identification of a key. * <p> * To check that the key matches some anonymousId, use {@link #matchAnonymousId(byte[])}. * <p> * Therefore, the private key fingerprint is its public key fingerprint. The public key fingerprint is calculated * using some hash over it's parameters, see {@link PublicKey#fingerprint()} * * @return */ public byte[] createAnonymousId() { byte[] rvector = Do.randomBytes(32); HMAC hmac = new HMAC(fingerprint()); hmac.update(rvector); byte[] result = new byte[64]; System.arraycopy(rvector, 0, result, 0, 32); System.arraycopy(hmac.digest(), 0, result, 32, 32); return result; }