public static byte[] computeIkm( final byte[] ecdhSecret, final byte[] authSecret, final byte[] uaPublic, final byte[] asPublic) throws GeneralSecurityException { byte[] keyInfo = Bytes.concat(WebPushConstants.IKM_INFO, uaPublic, asPublic); return Hkdf.computeHkdf( WebPushConstants.HMAC_SHA256, ecdhSecret /* ikm */, authSecret /* salt */, keyInfo, WebPushConstants.IKM_SIZE); }
/** * Transforms a passed LSB first byte array to an int * * @param bytes that should be transformed to a byte array * @param length amount of the passed {@code bytes} that should be transformed */ public static int byteArrayToInt(byte[] bytes, int length) { return byteArrayToInt(bytes, 0, length); }
static void verifyMac(final byte[] key, byte[] data, byte[] mac) throws GeneralSecurityException { if (!Bytes.equal(computeMac(key, data), mac)) { throw new GeneralSecurityException("invalid MAC"); } } }
/** * s2v per https://tools.ietf.org/html/rfc5297 * * @param s * @return s2v(si) * @throws GeneralSecurityException */ private byte[] s2v(final byte[]... s) throws GeneralSecurityException { if (s.length == 0) { // Should never happen with AES-SIV, but we include this for completeness. return cmacForS2V.computeMac(BLOCK_ONE); } byte[] result = cmacForS2V.computeMac(BLOCK_ZERO); for (int i = 0; i < s.length - 1; i++) { result = Bytes.xor(AesUtil.dbl(result), cmacForS2V.computeMac(s[i])); } byte[] lastBlock = s[s.length - 1]; if (lastBlock.length >= 16) { result = Bytes.xorEnd(lastBlock, result); } else { result = Bytes.xor(AesUtil.cmacPad(lastBlock), AesUtil.dbl(result)); } return cmacForS2V.computeMac(result); }
/** * Computes the xor of two byte arrays of equal size. * * @return a new byte[] of length x.length. */ public static final byte[] xor(final byte[] x, final byte[] y) { if (x.length != y.length) { throw new IllegalArgumentException("The lengths of x and y should match."); } return xor(x, 0, y, 0, x.length); }
private void process(final byte[] nonce, ByteBuffer output, ByteBuffer input) throws GeneralSecurityException { int length = input.remaining(); int numBlocks = (length / BLOCK_SIZE_IN_BYTES) + 1; for (int i = 0; i < numBlocks; i++) { ByteBuffer keyStreamBlock = getKeyStreamBlock(nonce, i + initialCounter); if (i == numBlocks - 1) { // last block Bytes.xor( output, input, keyStreamBlock, length % BLOCK_SIZE_IN_BYTES); } else { Bytes.xor( output, input, keyStreamBlock, BLOCK_SIZE_IN_BYTES); } } }
/** * Encrypts {@code plaintext} with {@code aad} as additional authenticated data. The resulting * ciphertext allows for checking authenticity and integrity of additional data ({@code aad}), but * does not guarantee its secrecy. * * <p>The plaintext is encrypted with an {@code IndCpaCipher}, then MAC is computed over (aad || * ciphertext || t) where t is aad's length in bits represented as 64-bit bigendian unsigned * integer. The final ciphertext format is (ind-cpa ciphertext || mac). * * @return resulting ciphertext. */ @Override public byte[] encrypt(final byte[] plaintext, final byte[] associatedData) throws GeneralSecurityException { byte[] ciphertext = cipher.encrypt(plaintext); byte[] aad = associatedData; if (aad == null) { aad = new byte[0]; } byte[] aadLengthInBits = Arrays.copyOf(ByteBuffer.allocate(8).putLong(8L * aad.length).array(), 8); byte[] macValue = mac.computeMac(Bytes.concat(aad, ciphertext, aadLengthInBits)); return Bytes.concat(ciphertext, macValue); }
@Override public void verifyMac(final byte[] mac, final byte[] data) throws GeneralSecurityException { if (!Bytes.equal(computeMac(data), mac)) { throw new GeneralSecurityException("invalid MAC"); } } }
mLast = Bytes.xor(data, (n - 1) * AesUtil.BLOCK_SIZE, subKey1, 0, AesUtil.BLOCK_SIZE); } else { mLast = Bytes.xor( AesUtil.cmacPad(Arrays.copyOfRange(data, (n - 1) * AesUtil.BLOCK_SIZE, data.length)), subKey2); y = Bytes.xor(x, 0, data, i * AesUtil.BLOCK_SIZE, AesUtil.BLOCK_SIZE); x = aes.doFinal(y); y = Bytes.xor(mLast, x);
/** * Transforms a passed LSB first byte array to an int * * @param bytes that should be transformed to a byte array */ public static int byteArrayToInt(byte[] bytes) { return byteArrayToInt(bytes, bytes.length); }
/** * Computes symmetric key for ECIES with HKDF from the provided parameters. * * @param ephemeralPublicKeyBytes the encoded ephemeral public key, i.e. the KEM part of the * hybrid encryption. In some versions of ECIES (e.g. IEEE P1363a) this argument is optional. * Shoup strongly prefers the inclusion of this argument in * http://eprint.iacr.org/2001/112.pdf (see discussion of the value C0 in Section 15.6, and * 15.6.1) * @param sharedSecret the shared DH secret. This typically is the x-coordinate of the secret * point. * @param hmacAlgo the HMAC used (e.g. "HmacSha256") * @param hkdfInfo TODO(bleichen): determine what are good values for Info and salt and what are * not good values. The ISO standard proposal http://eprint.iacr.org/2001/112.pdf does not * allow additional values for the key derivation (see Section 15.6.2) * @param hkdfSalt * @param keySizeInBytes the size of the key material for the DEM key. * @throws GeneralSecurityException if hmacAlgo is not supported */ public static byte[] computeEciesHkdfSymmetricKey( final byte[] ephemeralPublicKeyBytes, final byte[] sharedSecret, String hmacAlgo, final byte[] hkdfSalt, final byte[] hkdfInfo, int keySizeInBytes) throws GeneralSecurityException { byte[] hkdfInput = Bytes.concat(ephemeralPublicKeyBytes, sharedSecret); return Hkdf.computeHkdf(hmacAlgo, hkdfInput, hkdfSalt, hkdfInfo, keySizeInBytes); } }
@Override public void verifyMac(final byte[] mac, byte[] data) throws GeneralSecurityException { if (!Bytes.equal(mac, this.computeMac(data))) { throw new GeneralSecurityException("invalid MAC"); } }
@Override public byte[] encryptDeterministically(final byte[] plaintext, final byte[] associatedData) throws GeneralSecurityException { if (plaintext.length > Integer.MAX_VALUE - AesUtil.BLOCK_SIZE) { throw new GeneralSecurityException("plaintext too long"); } Cipher aesCtr = EngineFactory.CIPHER.getInstance("AES/CTR/NoPadding"); byte[] computedIv = s2v(associatedData, plaintext); byte[] ivForJavaCrypto = computedIv.clone(); ivForJavaCrypto[8] &= (byte) 0x7F; // 63th bit from the right ivForJavaCrypto[12] &= (byte) 0x7F; // 31st bit from the right aesCtr.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(this.aesCtrKey, "AES"), new IvParameterSpec(ivForJavaCrypto)); byte[] ctrCiphertext = aesCtr.doFinal(plaintext); return Bytes.concat(computedIv, ctrCiphertext); }
tagBuffer.get(expectedTag); assert expectedTag.length == tag.length; if (!Bytes.equal(expectedTag, tag)) { throw new GeneralSecurityException("Tag mismatch");
/** * Decrypts {@code ciphertext} with {@code aad} as additional authenticated data. The decryption * verifies the authenticity and integrity of additional data ({@code aad}), but there are no * guarantees wrt. secrecy of that data. * * <p>The ciphertext format is ciphertext || mac. The MAC is verified against (aad || ciphertext|| * t) where t is aad's length in bits represented as 64-bit bigendian unsigned integer. * * @return resulting plaintext. */ @Override public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData) throws GeneralSecurityException { if (ciphertext.length < macLength) { throw new GeneralSecurityException("ciphertext too short"); } byte[] rawCiphertext = Arrays.copyOfRange(ciphertext, 0, ciphertext.length - macLength); byte[] macValue = Arrays.copyOfRange(ciphertext, ciphertext.length - macLength, ciphertext.length); byte[] aad = associatedData; if (aad == null) { aad = new byte[0]; } byte[] aadLengthInBits = Arrays.copyOf(ByteBuffer.allocate(8).putLong(8L * aad.length).array(), 8); mac.verifyMac(macValue, Bytes.concat(aad, rawCiphertext, aadLengthInBits)); return cipher.decrypt(rawCiphertext); } }
if (Bytes.equal(expectedIv, computedIv)) { return decryptedPt; } else {
byte[] s = new byte[FIELD_LEN]; mulAdd(s, hram, hashedPrivateKey, r); return Bytes.concat(rB, s);
/** * Validates public key and clear its most significant bit. * * @throws InvalidKeyException iff the {@code pubKey} is in the banned list or its length is not * 32-byte. */ private static void validatePubKeyAndClearMsb(byte[] pubKey) throws InvalidKeyException { if (pubKey.length != 32) { throw new InvalidKeyException("Public key length is not 32-byte"); } // Clears the most significant bit as in the method decodeUCoordinate() of RFC7748. pubKey[31] &= (byte) 0x7f; for (int i = 0; i < BANNED_PUBLIC_KEYS.length; i++) { if (Bytes.equal(BANNED_PUBLIC_KEYS[i], pubKey)) { throw new InvalidKeyException("Banned public key: " + Hex.encode(BANNED_PUBLIC_KEYS[i])); } } }
@Override public byte[] computeMac(final byte[] data) throws GeneralSecurityException { if (primitives.getPrimary().getOutputPrefixType().equals(OutputPrefixType.LEGACY)) { return Bytes.concat( primitives.getPrimary().getIdentifier(), primitives.getPrimary().getPrimitive().computeMac(Bytes.concat(data, formatVersion))); } return Bytes.concat( primitives.getPrimary().getIdentifier(), primitives.getPrimary().getPrimitive().computeMac(data)); }
/** Checks that the point is on curve */ boolean isOnCurve() { long[] x2 = new long[LIMB_CNT]; Field25519.square(x2, x); long[] y2 = new long[LIMB_CNT]; Field25519.square(y2, y); long[] z2 = new long[LIMB_CNT]; Field25519.square(z2, z); long[] z4 = new long[LIMB_CNT]; Field25519.square(z4, z2); long[] lhs = new long[LIMB_CNT]; // lhs = y^2 - x^2 Field25519.sub(lhs, y2, x2); // lhs = z^2 * (y2 - x2) Field25519.mult(lhs, lhs, z2); long[] rhs = new long[LIMB_CNT]; // rhs = x^2 * y^2 Field25519.mult(rhs, x2, y2); // rhs = D * x^2 * y^2 Field25519.mult(rhs, rhs, D); // rhs = z^4 + D * x^2 * y^2 Field25519.sum(rhs, z4); // z^2 (y^2 - x^2) == z^4 + D * x^2 * y^2 return Bytes.equal(Field25519.contract(lhs), Field25519.contract(rhs)); } }