From a5491cb3dcd86383c242f517490781bb790fad61 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Fri, 7 Apr 2017 00:53:18 -0400 Subject: Shake bugs out of hal_rpc_pkey_import(). --- key-backup.py | 158 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 39 deletions(-) (limited to 'key-backup.py') diff --git a/key-backup.py b/key-backup.py index 41b8f10..3d971e7 100644 --- a/key-backup.py +++ b/key-backup.py @@ -34,75 +34,107 @@ from libhal import * from Crypto.PublicKey import RSA from Crypto.Cipher import AES, PKCS1_v1_5 -from Crypto.Util.asn1 import DerObject, DerSequence, DerNull, DerOctetString, DerObjectId +from Crypto.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId, DerNull from Crypto.Random import new as csprng from struct import pack, unpack +from atexit import register as atexit - -def dumpasn1(der): +def dumpasn1(der, flags = "-aop"): from subprocess import call from tempfile import NamedTemporaryFile with NamedTemporaryFile() as f: f.write(der) f.flush() - call(("dumpasn1", "-aop", f.name)) - -def t(x): - return filter(x.isType, x.typeTags)[0] + call(("dumpasn1", flags, f.name)) hal_asn1_oid_rsaEncryption = "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01" hal_asn1_oid_aesKeyWrap = "\x60\x86\x48\x01\x65\x03\x04\x01\x30" kek_length = 256/8 # We can determine this from the keywrap OID, this is for AES-256 -def main(): - kekek = RSA.importKey(kekek_pem) +hsm = None - from atexit import register as atexit +def main(): + global hsm hsm = HSM() #hsm.debug_io = args.io_log - hsm.login(HAL_USER_WHEEL, "fnord") atexit(hsm.logout) + test_export() + test_import() + +def test_export(): + print "Testing hal_rpc_pkey_export()" + + kekek = RSA.importKey(kekek_pem) - kekek_handle = hsm.pkey_load(type = HAL_KEY_TYPE_RSA_PUBLIC, - curve = HAL_CURVE_NONE, - der = kekek.publickey().exportKey(format = "DER")) + kekek_handle = hsm.pkey_load( + type = HAL_KEY_TYPE_RSA_PUBLIC, + curve = HAL_CURVE_NONE, + flags = HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT, + der = kekek.publickey().exportKey(format = "DER")) atexit(kekek_handle.delete) - print "KEKEK:", kekek_handle.uuid - pkey_handle = hsm.pkey_generate_ec(HAL_CURVE_P256, HAL_KEY_FLAG_EXPORTABLE) - atexit(pkey_handle.delete) - print "PKey: ", pkey_handle.uuid + pkey1 = hsm.pkey_generate_ec( + curve = HAL_CURVE_P256, + flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_EXPORTABLE) + atexit(pkey1.delete) - pkcs8_der, kek_der = kekek_handle.export_pkey(pkey_handle) + pkey2 = hsm.pkey_generate_rsa( + keylen= 2048, + flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_EXPORTABLE) + atexit(pkey2.delete) - pkcs8_alg, pkcs8_data = show_encrypted_pkcs8("PKCS #8:", pkcs8_der, hal_asn1_oid_aesKeyWrap) - kek_alg, kek_data = show_encrypted_pkcs8("KEK: ", kek_der, hal_asn1_oid_rsaEncryption) + for pkey in (pkey1, pkey2): + pkcs8_der, kek_der = kekek_handle.export_pkey(pkey) + kek = PKCS1_v1_5.new(kekek).decrypt( + parse_EncryptedPrivateKeyInfo(kek_der, hal_asn1_oid_rsaEncryption), + csprng().read(kek_length)) + der = AESKeyWrapWithPadding(kek).unwrap( + parse_EncryptedPrivateKeyInfo(pkcs8_der, hal_asn1_oid_aesKeyWrap)) + dumpasn1(der) - # Voodoo to defend against Bleichenbacher Attack per Crypto.Cipher.PKCS1_v1_5 API documentation - kek = PKCS1_v1_5.new(kekek).decrypt(kek_data, csprng().read(kek_length)) +def test_import(): + print "Testing hal_rpc_pkey_import()" - pkey = AESKeyWrapWithPadding(kek).unwrap(pkcs8_data) + if False: + kekek = RSA.importKey(kekek_pem) + kekek_handle = hsm.pkey_load( + type = HAL_KEY_TYPE_RSA_PRIVATE, + curve = HAL_CURVE_NONE, + flags = HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT, + der = kekek.exportKey(format = "DER", pkcs = 8)) + atexit(kekek_handle.delete) + kekek = kekek.publickey() - dumpasn1(pkey) + else: + kekek_handle = hsm.pkey_generate_rsa( + keylen= 2048, + flags = HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT) + atexit(kekek_handle.delete) + kekek = RSA.importKey(kekek_handle.public_key) + for der in (rsa_2048_der, ecdsa_p384_der): -def show_encrypted_pkcs8(label, der, oid): - print - print label - dumpasn1(der) - print - result = parse_encrypted_pkcs8(der) - for name, value in zip(("algorithm", "encryptedData"), result): - print "{:14s} {}".format(name, "-".join("{:02x}".format(ord(v)) for v in value)) - print - print "OID {}".format("matches" if result[0] == oid else "doesn't match") - return result + kek = csprng().read(kek_length) -def parse_encrypted_pkcs8(der): + pkey = kekek_handle.import_pkey( + pkcs8 = encode_EncryptedPrivateKeyInfo(AESKeyWrapWithPadding(kek).wrap(der), + hal_asn1_oid_aesKeyWrap), + kek = encode_EncryptedPrivateKeyInfo(PKCS1_v1_5.new(kekek).encrypt(kek), + hal_asn1_oid_rsaEncryption), + flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE) + + atexit(pkey.delete) + + print "Imported", pkey.uuid + dumpasn1(pkey.public_key) + dumpasn1(der) + + +def parse_EncryptedPrivateKeyInfo(der, oid): encryptedPrivateKeyInfo = DerSequence() encryptedPrivateKeyInfo.decode(der) @@ -113,10 +145,20 @@ def parse_encrypted_pkcs8(der): algorithm = DerObjectId() algorithm.decode(encryptionAlgorithm[0]) - encryptedData = DerObject() + encryptedData = DerOctetString() encryptedData.decode(encryptedPrivateKeyInfo[1]) - return algorithm.payload, encryptedData.payload + if algorithm.payload != oid: + raise ValueError + + return encryptedData.payload + + +def encode_EncryptedPrivateKeyInfo(der, oid): + return DerSequence([ + DerSequence([chr(0x06) + chr(len(oid)) + oid]).encode(), + DerOctetString(der).encode() + ]).encode() class AESKeyWrapWithPadding(object): @@ -228,5 +270,43 @@ L7en4sepnWifRGs2gnPYKrn1Zg== -----END PRIVATE KEY----- ''' +# Static keys for import testing. + +rsa_2048_der = '''\ +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwyEYARfw428GU +6XwflyOMJt1U+SM+H1zVmguXDqOX9i/aAhe8dvmYTokcxiWJ14N8dfbwKh3pyaBB +HenlaarQvINRYa812X7z/UeBBTNEmURWVCiGMq/ginxpyUSxjTxtOP/VRH8tYlwm +9K/L/7BN78bdQLeA6atFuuJkZDjvrLn7UypzCa/3Mip9kpu/6nJDnDUYkngwE4G5 +4J6yWO+/BEqoRFhVkDdtyDcyBOvZsV3Vqi8tvpNrLTujzFHAQD8EIT9r7IxP/me7 +We3Tu2i8CEnqUNiGxhoi5hzKq36NIDYrYvzg3xQNorS9SC3rHz/F3I9+XDqNnfYV +ok8CBFNFAgMBAAECggEBAIH0Z8kxqW1e1tqSHUXXxDD2LQSXNPoo8gSv/k8oWsiO +GLUpjqtjxq3ZJeA6JUREYosu6L26KE1BhAX6aIPV/tT9j4dWyQdMAJB6I4NMAFkw +VlUj/rpQLoxhIX5ej5n6Gm6sVR1BAkCpqtaUT1smdkOEvWrOdVdV7ysOa/ii2FwP +IzYbHoBOWeI/aq8RdtZ/NeQenPZX++VihRT7b4prfjxTbeghvN1NNy7TmCquBZSM +9aVMIB4Q4dXAdRePB6K6N0Gy60CQll62veppbFJHDPdjKjkxiOgIJo2XYIM5LziD +ta0VxHRNpTULCGwAA94f9yNo/YzpxLNw94COu0StLa0CgYEA4k0UHHSRb0jhkB0f +6jknEthhBWIFmJ8bDJ/ObN0wxLhluiajzelPS/YB2Qsdj8NGXSX5yIrrNyCc+tjk +wniFD8X52h3z4nEzrx0Hn2jqL6w8k2jp1WMZCGW7Ure6o5ilhb5vMUqjvHyVDsG6 +aAl/82oWWXV0HWEHHzjWgeNWpPMCgYEAx/uJx07z0TztvyEaJrBy/rvPtdOnZ03F +UiKAUFWS6C9vncmoH5m+fHaxKn7jPCCxHyoea3BKvqv5MIxkn3DJMmu9UVwLAGne +JnElygsA1ogy9f9YN+Fp1jXikdywbd2T0RsuoaUm+iMFO/fSPGM5qTbOTzY4dw4R +oSX/NmzjlOcCgYEAgss10nR1EjK3W8nZhlBeCwBQowHSZjGfOp6qejUlWK2S7hIj +HoG4ORkIXF+WSF7+rhui0IuqAwSwdjMhlFx/22v7SluBd+EhlBZdL389yyvrHu/G +JnTOJRJXQCm8j41MLY6xSXXwSKJgrFS/3h2PfCpWnIHMCKbprNv27r9sdo0CgYBg +UBGUDr84N1rdIQkiNvq7GiK4FD5cb0UoAHvBtOTys93SpUs2JOprsRI0QDYaQDht +pPBPmB43ZEW4DvVrIHuVr/PWmjimM1aNNxMXEmON7rx0Y0zOZN5/DyaWTy4dS4ik +Pa4gpZR3BaTAs+LpuHQNvdpwpdFd7UWqUc1vHdQhYwKBgAbz0tbrr/QTl6/WvbBS +6rALr2x7hueOmyCGzgk6o1gPkqbvuGZDJIkInzjYMxKly13pWQhFJlSZMlhpBosu +u/L+h81Pj1Ks38yzxnoz1QJ/s3xWfE77xFvz8u319Gv25Hf+SFkd97+BxF1Fq8tV +r/gYnWjq6Ay5HGptjovGzyYi +'''.decode("base64") + +ecdsa_p384_der = '''\ +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDB4+4rLfjwI6g5bIk4H +Glylc+ggvl7rBcFCxTY2K6Rd/ZuDto4ZOwxaQcvTyctOZaKhZANiAATaNzklDKdP +QYe2NhCvkoxirFCw3AKy767BCmPjad4ZfVNCchSRY+fKTgatEDtCly8+G2914q1w +/CdxWp+coDHxgG6zBV/y7KvtoO8cA5E3KE2jHZP8gwkzUe/SNx9Tx6U= +'''.decode("base64") + if __name__ == "__main__": main() -- cgit v1.2.3