RsaKemHybridDecrypt implements HybridDecrypt { @Override public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo) throws GeneralSecurityException { int modSizeInBytes = RsaKem.bigIntSizeInBytes(recipientPrivateKey.getModulus()); if (ciphertext.length < modSizeInBytes) { throw new GeneralSecurityException( String.format( "Ciphertext must be of at least size %d bytes, but got %d", modSizeInBytes, ciphertext.length)); } ByteBuffer cipherBuffer = ByteBuffer.wrap(ciphertext); byte[] token = new byte[modSizeInBytes]; cipherBuffer.get(token); Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding"); rsaCipher.init(Cipher.DECRYPT_MODE, recipientPrivateKey); byte[] sharedSecret = rsaCipher.doFinal(token); byte[] demKey = Hkdf.computeHkdf( hkdfHmacAlgo, sharedSecret, hkdfSalt, contextInfo, aeadFactory.getKeySizeInBytes()); Aead aead = aeadFactory.createAead(demKey); byte[] demPayload = new byte[cipherBuffer.remaining()]; cipherBuffer.get(demPayload); return aead.decrypt(demPayload, RsaKem.EMPTY_AAD); } RsaKemHybridDecrypt(
      final RSAPrivateKey recipientPrivateKey,
      String hkdfHmacAlgo,
      final byte[] hkdfSalt,
      AeadFactory aeadFactory); @Override byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo);  }
@Test public void decrypt_modifiedCiphertext() throws GeneralSecurityException { if (TestUtil.isTsan()) { return; } KeyPair keyPair = RsaKem.generateRsaKeyPair(2048); String hmacAlgo = "HMACSHA256"; byte[] salt = Random.randBytes(20); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); HybridEncrypt hybridEncrypt = new RsaKemHybridEncrypt(rsaPublicKey, hmacAlgo, salt, new AesGcmFactory(16)); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); HybridDecrypt hybridDecrypt = new RsaKemHybridDecrypt(rsaPrivateKey, hmacAlgo, salt, new AesGcmFactory(16)); byte[] plaintext = Random.randBytes(20); byte[] context = Random.randBytes(20); byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); for (TestUtil.BytesMutation mutation : TestUtil.generateMutations(ciphertext)) { assertThrows( GeneralSecurityException.class, () -> hybridDecrypt.decrypt(mutation.value, context)); } }
@Test public void decrypt_modifiedContext() throws GeneralSecurityException { if (TestUtil.isTsan()) { return; } KeyPair keyPair = RsaKem.generateRsaKeyPair(2048); String hmacAlgo = "HMACSHA256"; byte[] salt = Random.randBytes(20); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); HybridEncrypt hybridEncrypt = new RsaKemHybridEncrypt(rsaPublicKey, hmacAlgo, salt, new AesGcmFactory(16)); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); HybridDecrypt hybridDecrypt = new RsaKemHybridDecrypt(rsaPrivateKey, hmacAlgo, salt, new AesGcmFactory(16)); byte[] plaintext = Random.randBytes(20); byte[] context = Random.randBytes(20); byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); for (TestUtil.BytesMutation mutation : TestUtil.generateMutations(context)) { assertThrows( GeneralSecurityException.class, () -> hybridDecrypt.decrypt(ciphertext, mutation.value)); } }
@Test public void decrypt_modifiedSalt() throws GeneralSecurityException { if (TestUtil.isTsan()) { return; } KeyPair keyPair = RsaKem.generateRsaKeyPair(2048); String hmacAlgo = "HMACSHA256"; byte[] salt = Random.randBytes(20); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); HybridEncrypt hybridEncrypt = new RsaKemHybridEncrypt(rsaPublicKey, hmacAlgo, salt, new AesGcmFactory(16)); byte[] plaintext = Random.randBytes(20); byte[] context = Random.randBytes(20); byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); for (int bytes = 0; bytes < salt.length; bytes++) { for (int bit = 0; bit < 8; bit++) { byte[] modifiedSalt = Arrays.copyOf(salt, salt.length); modifiedSalt[bytes] ^= (byte) (1 << bit); HybridDecrypt hybridDecrypt = new RsaKemHybridDecrypt(rsaPrivateKey, hmacAlgo, modifiedSalt, new AesGcmFactory(16)); assertThrows( GeneralSecurityException.class, () -> hybridDecrypt.decrypt(ciphertext, context)); } } }
@Test public void decryptWithTestVectors() throws GeneralSecurityException { for (RsaKemHybridTestVector vector : rsaKemHybridTestVectors) { String hmacAlgo = vector.hmacAlgo; byte[] salt = vector.salt; RSAPrivateKey rsaPrivateKey = vector.privateKey; HybridDecrypt hybridDecrypt = new RsaKemHybridDecrypt( rsaPrivateKey, hmacAlgo, salt, new AesGcmFactory(vector.aesGcmKeySizeInBytes)); byte[] plaintext = hybridDecrypt.decrypt(vector.ciphertext, vector.contextInfo); assertThat(Hex.encode(plaintext)).isEqualTo(vector.plaintext); } }