def test_convert_to_raw_fails(self, template_name): """Make sure that "wrapped" AEADs don't accept the raw AEAD ciphertext.""" supported_langs = supported_key_types.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[ template_name] self.assertNotEmpty(supported_langs) template = supported_key_types.KEY_TEMPLATE[template_name] if template.output_prefix_type == tink_pb2.RAW: # ciphertext is already raw, so there is nothing to test. return keyset = testing_servers.new_keyset(supported_langs[0], template) ciphertext = testing_servers.aead(supported_langs[0], keyset).encrypt( b'plaintext', b'aad') for lang in supported_langs: primitive = testing_servers.aead(lang, keyset) # all non-raw keys add a 5 byte prefix to the raw ciphertext. Remove it # and try to decrypt. raw_ciphertext = ciphertext[:5] with self.assertRaises( tink.TinkError, msg= 'non-raw ciphertext with 5 byte tink header removed did not ' 'cause a decryption error in %s. keyset="%s", ' 'modified_ciphertext="%s"' % (lang, keyset.hex(), raw_ciphertext.hex())): primitive.decrypt(raw_ciphertext, b'aad')
def test_encrypt_decrypt(self, key_template_name, supported_langs): self.assertNotEmpty(supported_langs) key_template = supported_key_types.KEY_TEMPLATE[key_template_name] # Take the first supported language to generate the keyset. keyset = testing_servers.new_keyset(supported_langs[0], key_template) supported_aeads = [ testing_servers.aead(lang, keyset) for lang in supported_langs ] unsupported_aeads = [ testing_servers.aead(lang, keyset) for lang in SUPPORTED_LANGUAGES if lang not in supported_langs ] for p in supported_aeads: plaintext = ( b'This is some plaintext message to be encrypted using key_template ' b'%s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) associated_data = ( b'Some associated data for %s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) ciphertext = p.encrypt(plaintext, associated_data) for p2 in supported_aeads: output = p2.decrypt(ciphertext, associated_data) self.assertEqual(output, plaintext) for p2 in unsupported_aeads: with self.assertRaises(tink.TinkError): p2.decrypt(ciphertext, associated_data) for p in unsupported_aeads: with self.assertRaises(tink.TinkError): p.encrypt(b'plaintext', b'associated_data')
def test_encrypt_decrypt(self, key_template_name, supported_langs): key_template = supported_key_types.KEY_TEMPLATE[key_template_name] # use java to generate keys, as it supports all key types. keyset_handle = testing_servers.new_keyset_handle('java', key_template) supported_aeads = [ testing_servers.aead(lang, keyset_handle) for lang in supported_langs ] unsupported_aeads = [ testing_servers.aead(lang, keyset_handle) for lang in testing_servers.LANGUAGES if lang not in supported_langs ] for p in supported_aeads: plaintext = ( b'This is some plaintext message to be encrypted using key_template ' b'%s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) associated_data = ( b'Some associated data for %s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) ciphertext = p.encrypt(plaintext, associated_data) for p2 in supported_aeads: output = p2.decrypt(ciphertext, associated_data) self.assertEqual(output, plaintext) for p2 in unsupported_aeads: with self.assertRaises(tink.TinkError): p2.decrypt(ciphertext, associated_data) for p in unsupported_aeads: with self.assertRaises(tink.TinkError): p.encrypt(b'plaintext', b'associated_data')
def test_inc_version_aead(self, key_template_name, lang): """Increments the key version by one and checks they can't be used.""" template = supported_key_types.KEY_TEMPLATE[key_template_name] keyset = testing_servers.new_keyset(lang, template) _ = testing_servers.aead(lang, keyset).encrypt(b'foo', b'bar') for keyset1 in gen_inc_versions(keyset): aead_primitive = testing_servers.aead(lang, keyset1) with self.assertRaises(tink.TinkError): _ = aead_primitive.encrypt(b'foo', b'bar')
def test_aead_without_primary(self, key_template_name, lang): """Unsets the primary key and tries to use an AEAD primitive.""" template = supported_key_types.KEY_TEMPLATE[key_template_name] keyset = testing_servers.new_keyset(lang, template) ciphertext = testing_servers.aead(lang, keyset).encrypt(b'foo', b'bar') aead_without_primary = testing_servers.aead(lang, unset_primary(keyset)) with self.assertRaises(tink.TinkError): _ = aead_without_primary.encrypt(b'foo', b'bar') with self.assertRaises(tink.TinkError): _ = aead_without_primary.decrypt(ciphertext, b'bar')
def test_encrypt_decrypt(self, key_template_name): if key_template_name in _ADDITIONAL_KEY_TEMPLATES: key_template, key_type = _ADDITIONAL_KEY_TEMPLATES[ key_template_name] supported_langs = supported_key_types.SUPPORTED_LANGUAGES[key_type] else: key_template = supported_key_types.KEY_TEMPLATE[key_template_name] supported_langs = ( supported_key_types. SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[key_template_name]) self.assertNotEmpty(supported_langs) # Take the first supported language to generate the keyset. keyset = testing_servers.new_keyset(supported_langs[0], key_template) supported_aeads = [ testing_servers.aead(lang, keyset) for lang in supported_langs ] unsupported_aeads = [ testing_servers.aead(lang, keyset) for lang in SUPPORTED_LANGUAGES if lang not in supported_langs ] for p in supported_aeads: plaintext = ( b'This is some plaintext message to be encrypted using key_template ' b'%s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) associated_data = ( b'Some associated data for %s using %s for encryption.' % (key_template_name.encode('utf8'), p.lang.encode('utf8'))) ciphertext = p.encrypt(plaintext, associated_data) for p2 in supported_aeads: output = p2.decrypt(ciphertext, associated_data) self.assertEqual(output, plaintext) for p2 in unsupported_aeads: with self.assertRaises( tink.TinkError, msg= 'Language %s supports AEAD decrypt with %s unexpectedly' % (p2.lang, key_template_name)): p2.decrypt(ciphertext, associated_data) for p in unsupported_aeads: with self.assertRaises( tink.TinkError, msg='Language %s supports AEAD encrypt with %s unexpectedly' % (p.lang, key_template_name)): p.encrypt(b'plaintext', b'associated_data')
def test_inc_version_aead_aes_ctr_hmac_subkeys(self): """Increments the subkey versions by one and check they can't be used.""" template = supported_key_types.KEY_TEMPLATE['AES128_CTR_HMAC_SHA256'] for lang in TYPE_URL_TO_SUPPORTED_LANGUAGES[template.type_url]: keyset = testing_servers.new_keyset(lang, template) for keyset1 in gen_keys_for_aes_ctr_hmac_aead(keyset): aead_primitive = testing_servers.aead(lang, keyset1) with self.assertRaises(tink.TinkError): _ = aead_primitive.encrypt(b'foo', b'bar')
def test_inc_version_aead(self, name, key_class): """Increments the key version by one and checks they can't be used.""" template = supported_key_types.KEY_TEMPLATE[name] for lang in TYPE_URL_TO_SUPPORTED_LANGUAGES[template.type_url]: keyset = testing_servers.new_keyset(lang, template) keyset1 = inc_version(keyset, key_class) aead_primitive = testing_servers.aead(lang, keyset1) with self.assertRaises(tink.TinkError): _ = aead_primitive.encrypt(b'foo', b'bar')
def test_key_rotation(self, enc_lang, dec_lang, old_key_tmpl, new_key_tmpl): # Do a key rotation from an old key generated from old_key_tmpl to a new # key generated from new_key_tmpl. Encryption and decryption are done # in languages enc_lang and dec_lang. builder = keyset_builder.new_keyset_builder() older_key_id = builder.add_new_key(old_key_tmpl) builder.set_primary_key(older_key_id) enc_aead1 = testing_servers.aead(enc_lang, builder.keyset()) dec_aead1 = testing_servers.aead(dec_lang, builder.keyset()) newer_key_id = builder.add_new_key(new_key_tmpl) enc_aead2 = testing_servers.aead(enc_lang, builder.keyset()) dec_aead2 = testing_servers.aead(dec_lang, builder.keyset()) builder.set_primary_key(newer_key_id) enc_aead3 = testing_servers.aead(enc_lang, builder.keyset()) dec_aead3 = testing_servers.aead(dec_lang, builder.keyset()) builder.disable_key(older_key_id) enc_aead4 = testing_servers.aead(enc_lang, builder.keyset()) dec_aead4 = testing_servers.aead(dec_lang, builder.keyset()) self.assertNotEqual(older_key_id, newer_key_id) # 1 encrypts with the older key. So 1, 2 and 3 can decrypt it, but not 4. ciphertext1 = enc_aead1.encrypt(b'plaintext', b'ad') self.assertEqual(dec_aead1.decrypt(ciphertext1, b'ad'), b'plaintext') self.assertEqual(dec_aead2.decrypt(ciphertext1, b'ad'), b'plaintext') self.assertEqual(dec_aead3.decrypt(ciphertext1, b'ad'), b'plaintext') with self.assertRaises(tink.TinkError): _ = dec_aead4.decrypt(ciphertext1, b'ad') # 2 encrypts with the older key. So 1, 2 and 3 can decrypt it, but not 4. ciphertext2 = enc_aead2.encrypt(b'plaintext', b'ad') self.assertEqual(dec_aead1.decrypt(ciphertext2, b'ad'), b'plaintext') self.assertEqual(dec_aead2.decrypt(ciphertext2, b'ad'), b'plaintext') self.assertEqual(dec_aead3.decrypt(ciphertext2, b'ad'), b'plaintext') with self.assertRaises(tink.TinkError): _ = dec_aead4.decrypt(ciphertext2, b'ad') # 3 encrypts with the newer key. So 2, 3 and 4 can decrypt it, but not 1. ciphertext3 = enc_aead3.encrypt(b'plaintext', b'ad') with self.assertRaises(tink.TinkError): _ = dec_aead1.decrypt(ciphertext3, b'ad') self.assertEqual(dec_aead2.decrypt(ciphertext3, b'ad'), b'plaintext') self.assertEqual(dec_aead3.decrypt(ciphertext3, b'ad'), b'plaintext') self.assertEqual(dec_aead4.decrypt(ciphertext3, b'ad'), b'plaintext') # 4 encrypts with the newer key. So 2, 3 and 4 can decrypt it, but not 1. ciphertext4 = enc_aead4.encrypt(b'plaintext', b'ad') with self.assertRaises(tink.TinkError): _ = dec_aead1.decrypt(ciphertext4, b'ad') self.assertEqual(dec_aead2.decrypt(ciphertext4, b'ad'), b'plaintext') self.assertEqual(dec_aead3.decrypt(ciphertext4, b'ad'), b'plaintext') self.assertEqual(dec_aead4.decrypt(ciphertext4, b'ad'), b'plaintext')
def test_aead(self, lang): keyset = testing_servers.new_keyset(lang, aead.aead_key_templates.AES128_GCM) plaintext = b'The quick brown fox jumps over the lazy dog' associated_data = b'associated_data' aead_primitive = testing_servers.aead(lang, keyset) ciphertext = aead_primitive.encrypt(plaintext, associated_data) output = aead_primitive.decrypt(ciphertext, associated_data) self.assertEqual(output, plaintext) with self.assertRaises(tink.TinkError): aead_primitive.decrypt(b'foo', associated_data)
def test_decrypt_modified_ciphertext_fails(self, template_name): """A basic test if the ciphertext is malleable with single bit-flips.""" supported_langs = supported_key_types.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[ template_name] self.assertNotEmpty(supported_langs) template = supported_key_types.KEY_TEMPLATE[template_name] # Take the first supported language to generate the keyset. keyset = testing_servers.new_keyset(supported_langs[0], template) ciphertext = testing_servers.aead(supported_langs[0], keyset).encrypt( b'plaintext', b'aad') for lang in supported_langs: primitive = testing_servers.aead(lang, keyset) for i in range(len(ciphertext) * 8): # flip the ith bit in the ciphertext. modified_ciphertext = bytearray(ciphertext) modified_ciphertext[i // 8] ^= 1 << (i % 8) with self.assertRaises( tink.TinkError, msg='ciphertext with the %dth bit flipped did not cause a ' 'decryption error in %s. keyset="%s", modified_ciphertext="%s"' % (i, lang, keyset.hex(), bytes(modified_ciphertext).hex())): primitive.decrypt(bytes(modified_ciphertext), b'aad')
def test_keyset_validation_consistency(self, name, keyset): supported_langs = supported_key_types.SUPPORTED_LANGUAGES[ supported_key_types.KEY_TYPE_FROM_URL[ keyset.key[0].key_data.type_url]] supported_aeads = [ testing_servers.aead(lang, keyset.SerializeToString()) for lang in supported_langs ] plaintext = b'plaintext' associated_data = b'associated_data' failures = 0 ciphertexts = {} results = {} for p in supported_aeads: try: ciphertexts[p.lang] = p.encrypt(plaintext, associated_data) if (name, p.lang) in SUCCEEDS_BUT_SHOULD_FAIL: failures += 1 del ciphertexts[p.lang] if (name, p.lang) in FAILS_BUT_SHOULD_SUCCEED: self.fail( '(%s, %s) succeeded, but is in FAILS_BUT_SHOULD_SUCCEED' % (name, p.lang)) results[p.lang] = 'success' except tink.TinkError as e: if (name, p.lang) not in FAILS_BUT_SHOULD_SUCCEED: failures += 1 if (name, p.lang) in SUCCEEDS_BUT_SHOULD_FAIL: self.fail( '(%s, %s) is in SUCCEEDS_BUT_SHOULD_FAIL, but failed with %s' % (name, p.lang, e)) results[p.lang] = e # Test that either all supported langs accept the key, or all reject. if failures not in [0, len(supported_langs)]: self.fail('encryption for key %s is inconsistent: %s' % (name, results)) # Test all generated ciphertexts can be decypted. for enc_lang, ciphertext in ciphertexts.items(): dec_aead = supported_aeads[0] output = dec_aead.decrypt(ciphertext, associated_data) if output != plaintext: self.fail( 'ciphertext encrypted with key %s in lang %s could not be' 'decrypted in lang %s.' % (name, enc_lang, dec_aead.lang))