AesGcmSiv implements Aead { @Override public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData) throws GeneralSecurityException { if (ciphertext.length < IV_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES) { throw new GeneralSecurityException("ciphertext too short"); } AlgorithmParameterSpec params = getParams(ciphertext, 0, IV_SIZE_IN_BYTES); localCipher.get().init(Cipher.DECRYPT_MODE, keySpec, params); if (associatedData != null && associatedData.length != 0) { localCipher.get().updateAAD(associatedData); } return localCipher .get() .doFinal(ciphertext, IV_SIZE_IN_BYTES, ciphertext.length - IV_SIZE_IN_BYTES); } AesGcmSiv(final byte[] key); @Override /** * On Android KitKat (API level 19) this method does not support non null or non empty {@code * associatedData}. It might not work at all in older versions. */ byte[] encrypt(final byte[] plaintext, final byte[] associatedData); @Override /** * On Android KitKat (API level 19) this method does not support non null or non empty {@code * associatedData}. It might not work at all in older versions. */ byte[] decrypt(final byte[] ciphertext, final byte[] associatedData);  }
@Test public void testWycheproofVectors() throws Exception { JSONObject json = WycheproofTestUtil.readJson("../wycheproof/testvectors/aes_gcm_siv_test.json"); int errors = 0; int cntSkippedTests = 0; JSONArray testGroups = json.getJSONArray("testGroups"); for (int i = 0; i < testGroups.length(); i++) { JSONObject group = testGroups.getJSONObject(i); int keySize = group.getInt("keySize"); JSONArray tests = group.getJSONArray("tests"); if (!Arrays.asList(keySizeInBytes).contains(keySize / 8)) { cntSkippedTests += tests.length(); continue; } for (int j = 0; j < tests.length(); j++) { JSONObject testcase = tests.getJSONObject(j); String tcId = String.format( "testcase %d (%s)", testcase.getInt("tcId"), testcase.getString("comment")); byte[] iv = Hex.decode(testcase.getString("iv")); byte[] key = Hex.decode(testcase.getString("key")); byte[] msg = Hex.decode(testcase.getString("msg")); byte[] aad = Hex.decode(testcase.getString("aad")); byte[] ct = Hex.decode(testcase.getString("ct")); byte[] tag = Hex.decode(testcase.getString("tag")); byte[] ciphertext = Bytes.concat(iv, ct, tag); String result = testcase.getString("result"); if (iv.length != 12) { result = "invalid"; } try { AesGcmSiv gcm = new AesGcmSiv(key); byte[] decrypted = gcm.decrypt(ciphertext, aad); boolean eq = TestUtil.arrayEquals(decrypted, msg); if (result.equals("invalid")) { System.out.printf( "FAIL %s: accepting invalid ciphertext, cleartext: %s, decrypted: %s%n", tcId, Hex.encode(msg), Hex.encode(decrypted)); errors++; } else { if (!eq) { System.out.printf( "FAIL %s: incorrect decryption, result: %s, expected: %s%n", tcId, Hex.encode(decrypted), Hex.encode(msg)); errors++; } } } catch (GeneralSecurityException ex) { if (result.equals("valid")) { System.out.printf("FAIL %s: cannot decrypt, exception %s%n", tcId, ex); errors++; } } } } System.out.printf("Number of tests skipped: %d", cntSkippedTests); assertEquals(0, errors); }