aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2022-01-04 09:12:47 -0500
committerRob Austein <sra@hactrn.net>2022-01-04 09:12:47 -0500
commit6f0d8236b8622a68f42284ed1314d8acd86c89ed (patch)
tree1e0605d51d81fb87518eae0339576382e00fce0f
parentf8c3655b7af461555b89f7394c396b7ed7a267ee (diff)
Replace old PyCrypto with PyCryptodomeHEADmaster
PyCrypto is no longer present in Debian Bullseye and is abandonware in anycase. PyCryptodome is about 98% of a drop-in replacement (but that last 2% can be tricky), so convert the most critical stuff to use PyCryptodome. A bunch of the test scripts and so forth still need to be converted, for today the goals are just to have the package install properly and to be able to run the unit tests.
-rw-r--r--cryptech/libhal.py27
-rwxr-xr-xcryptech_backup16
-rw-r--r--unit-tests.py72
3 files changed, 60 insertions, 55 deletions
diff --git a/cryptech/libhal.py b/cryptech/libhal.py
index 102e663..105dd02 100644
--- a/cryptech/libhal.py
+++ b/cryptech/libhal.py
@@ -347,7 +347,8 @@ class LocalDigest(object):
"""
def __init__(self, hsm, handle, algorithm, key):
- from Crypto.Hash import HMAC, SHA, SHA224, SHA256, SHA384, SHA512
+ from Cryptodome.Hash import HMAC, SHA1, SHA224, SHA256, SHA384, SHA512
+ from Cryptodome.Util.asn1 import DerObjectId
from struct import pack
self.hsm = hsm
self.handle = handle
@@ -356,16 +357,22 @@ class LocalDigest(object):
h = self._algorithms[algorithm]
except AttributeError:
self._algorithms = {
- HAL_DIGEST_ALGORITHM_SHA1 : SHA.SHA1Hash,
- HAL_DIGEST_ALGORITHM_SHA224 : SHA224.SHA224Hash,
- HAL_DIGEST_ALGORITHM_SHA256 : SHA256.SHA256Hash,
- HAL_DIGEST_ALGORITHM_SHA384 : SHA384.SHA384Hash,
- HAL_DIGEST_ALGORITHM_SHA512 : SHA512.SHA512Hash
+ HAL_DIGEST_ALGORITHM_SHA1 : SHA1,
+ HAL_DIGEST_ALGORITHM_SHA224 : SHA224,
+ HAL_DIGEST_ALGORITHM_SHA256 : SHA256,
+ HAL_DIGEST_ALGORITHM_SHA384 : SHA384,
+ HAL_DIGEST_ALGORITHM_SHA512 : SHA512
}
h = self._algorithms[algorithm]
self.digest_length = h.digest_size
- self.algorithm_id = pack("BB", 0x30, 2 + len(h.oid)) + h.oid
- self._context = HMAC.HMAC(key = key, digestmod = h) if key else h()
+ if key:
+ self._context = HMAC.new(key = key, digestmod = h)
+ oid = h.new().oid
+ else:
+ self._context = h.new()
+ oid = self._context.oid
+ self.oid = DerObjectId(oid).encode()
+ self.algorithm_id = pack("BB", 0x30, 2 + len(self.oid)) + self.oid
def update(self, data):
self._context.update(data)
@@ -377,8 +384,8 @@ class LocalDigest(object):
if pkey.key_type not in (HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_RSA_PUBLIC):
return self.finalize()
# PKCS #1.5 requires the digest to be wrapped up in an ASN.1 DigestInfo object.
- from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString
- return DerSequence([DerSequence([self._context.oid, DerNull().encode()]).encode(),
+ from Cryptodome.Util.asn1 import DerSequence, DerNull, DerOctetString
+ return DerSequence([DerSequence([self.oid, DerNull().encode()]).encode(),
DerOctetString(self.finalize()).encode()]).encode()
diff --git a/cryptech_backup b/cryptech_backup
index a15c9c0..99d2c38 100755
--- a/cryptech_backup
+++ b/cryptech_backup
@@ -21,7 +21,7 @@ We also implement a software-based variant on this backup mechanism,
for cases where there is no second HSM. The protocol is much the
same, but the KEKEK is generated in software and encrypted using a
symmetric key derived from a passphrase using PBKDF2. This requires
-the PyCrypto library, and is only as secure as memory on the machine
+the PyCryptodome library, and is only as secure as memory on the machine
where you're running it (so it's theoretically vulnerable to root or
anybody with access to /dev/mem). Don't use this mode unless you
understand the risks, and see the "NOTE WELL" above.
@@ -305,7 +305,7 @@ class AESKeyWrapWithPadding(object):
"Something went wrong during unwrap."
def __init__(self, key):
- from Crypto.Cipher import AES
+ from Cryptodome.Cipher import AES
self.ctx = AES.new(key, AES.MODE_ECB)
def _encrypt(self, b1, b2):
@@ -391,7 +391,7 @@ class SoftKEKEK(object):
time.clock = time.process_time
def parse_EncryptedPrivateKeyInfo(self, der):
- from Crypto.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId
+ from Cryptodome.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId
encryptedPrivateKeyInfo = DerSequence()
encryptedPrivateKeyInfo.decode(der)
encryptionAlgorithm = DerSequence()
@@ -405,7 +405,7 @@ class SoftKEKEK(object):
return encryptedData.payload
def encode_EncryptedPrivateKeyInfo(self, der):
- from Crypto.Util.asn1 import DerSequence, DerOctetString
+ from Cryptodome.Util.asn1 import DerSequence, DerOctetString
return DerSequence([
DerSequence([
struct.pack("BB", 0x06, len(self.oid_aesKeyWrap)) + self.oid_aesKeyWrap
@@ -414,12 +414,12 @@ class SoftKEKEK(object):
]).encode()
def gen_salt(self, bytes = 16):
- from Crypto import Random
+ from Cryptodome import Random
return Random.new().read(bytes)
def wrapper(self, salt, keylen = 256, iterations = 8000):
- from Crypto.Protocol.KDF import PBKDF2
- from Crypto.Hash import SHA256, HMAC
+ from Cryptodome.Protocol.KDF import PBKDF2
+ from Cryptodome.Hash import SHA256, HMAC
return AESKeyWrapWithPadding(PBKDF2(
password = getpass.getpass("KEKEK Passphrase: "),
salt = salt,
@@ -433,7 +433,7 @@ class SoftKEKEK(object):
@classmethod
def generate(cls, args, result):
- from Crypto.PublicKey import RSA
+ from Cryptodome.PublicKey import RSA
self = cls()
k = RSA.generate(args.keylen)
salt = self.gen_salt()
diff --git a/unit-tests.py b/unit-tests.py
index 526379c..bdb6f77 100644
--- a/unit-tests.py
+++ b/unit-tests.py
@@ -49,19 +49,17 @@ from struct import pack, unpack
from cryptech.libhal import *
try:
- from Crypto.Util.number import inverse
- from Crypto.PublicKey import RSA
- from Crypto.Cipher import AES
- from Crypto.Cipher.PKCS1_v1_5 import PKCS115_Cipher
- from Crypto.Signature.PKCS1_v1_5 import PKCS115_SigScheme
- from Crypto.Hash.SHA256 import SHA256Hash as SHA256
- from Crypto.Hash.SHA384 import SHA384Hash as SHA384
- from Crypto.Hash.SHA512 import SHA512Hash as SHA512
+ from Cryptodome.Util.number import inverse
+ from Cryptodome.PublicKey import RSA
+ from Cryptodome.Cipher import AES, PKCS1_v1_5
+ from Cryptodome.Signature import pkcs1_15
+ from Cryptodome.Hash.SHA256 import new as SHA256
+ from Cryptodome.Hash.SHA384 import new as SHA384
+ from Cryptodome.Hash.SHA512 import new as SHA512
pycrypto_loaded = True
except ImportError:
pycrypto_loaded = False
-
try:
from ecdsa import der as ECDSA_DER
from ecdsa.keys import SigningKey as ECDSA_SigningKey
@@ -998,15 +996,15 @@ class TestPKeyHashing(TestCaseLoggedIn):
k1.verify(signature = sig, hash = self.h(alg, mixed_mode = True))
k2.verify(signature = sig, hash = self.h(alg, mixed_mode = True))
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_1024_sha256_data(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_data)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_2048_sha384_data(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_data)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_4096_sha512_data(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_data)
@@ -1022,15 +1020,15 @@ class TestPKeyHashing(TestCaseLoggedIn):
def test_load_sign_verify_ecdsa_p521_sha512_data(self):
self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_data)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_1024_sha256_remote_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_remote_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_2048_sha384_remote_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_remote_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_4096_sha512_remote_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_remote_remote)
@@ -1046,15 +1044,15 @@ class TestPKeyHashing(TestCaseLoggedIn):
def test_load_sign_verify_ecdsa_p521_sha512_remote_remote(self):
self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_remote_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_1024_sha256_remote_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_remote_local)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_2048_sha384_remote_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_remote_local)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_4096_sha512_remote_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_remote_local)
@@ -1070,15 +1068,15 @@ class TestPKeyHashing(TestCaseLoggedIn):
def test_load_sign_verify_ecdsa_p521_sha512_remote_local(self):
self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_remote_local)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_1024_sha256_local_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_local_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_2048_sha384_local_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_local_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_4096_sha512_local_remote(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_local_remote)
@@ -1094,15 +1092,15 @@ class TestPKeyHashing(TestCaseLoggedIn):
def test_load_sign_verify_ecdsa_p521_sha512_local_remote(self):
self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_local_remote)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_1024_sha256_local_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_local_local)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_2048_sha384_local_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_local_local)
- @unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+ @unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
def test_load_sign_verify_rsa_4096_sha512_local_local(self):
self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_local_local)
@@ -1131,7 +1129,7 @@ class TestPKeyHashing(TestCaseLoggedIn):
self.gen_sign_verify_hashsig(1, HAL_LMS_SHA256_N32_H5, HAL_LMOTS_SHA256_N32_W4, 2352, self.sign_verify_local_local)
-@unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+@unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
class TestPKeyRSAInterop(TestCaseLoggedIn):
@staticmethod
@@ -1575,7 +1573,7 @@ class TestPkeyECDSAVerificationNIST(TestCaseLoggedIn):
py_hash = SHA384)
-@unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package")
+@unittest.skipUnless(pycrypto_loaded, "Requires Python Cryptodome package")
class TestPKeyBackup(TestCaseLoggedIn):
oid_rsaEncryption = b"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
@@ -1583,7 +1581,7 @@ class TestPKeyBackup(TestCaseLoggedIn):
@staticmethod
def parse_EncryptedPrivateKeyInfo(der, oid):
- from Crypto.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId
+ from Cryptodome.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId
encryptedPrivateKeyInfo = DerSequence()
encryptedPrivateKeyInfo.decode(der)
encryptionAlgorithm = DerSequence()
@@ -1606,7 +1604,7 @@ class TestPKeyBackup(TestCaseLoggedIn):
@staticmethod
def encode_EncryptedPrivateKeyInfo(der, oid):
- from Crypto.Util.asn1 import DerSequence, DerOctetString
+ from Cryptodome.Util.asn1 import DerSequence, DerOctetString
return DerSequence([
DerSequence([pack("BB", 0x06, len(oid)) + oid]).encode(),
DerOctetString(der).encode()
@@ -1614,15 +1612,15 @@ class TestPKeyBackup(TestCaseLoggedIn):
@staticmethod
def make_kek():
- import Crypto.Random
- return Crypto.Random.new().read(256//8)
+ import Cryptodome.Random
+ return Cryptodome.Random.new().read(256//8)
def sig_check(self, pkey, der):
- from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString
- p115 = PKCS115_SigScheme(RSA.importKey(der))
+ from Cryptodome.Util.asn1 import DerSequence, DerNull, DerOctetString, DerObjectId
+ p115 = pkcs1_15.new(RSA.importKey(der))
hash = SHA256("Your mother was a hamster".encode())
data = DerSequence([
- DerSequence([hash.oid, DerNull().encode()]).encode(),
+ DerSequence([DerObjectId(hash.oid).encode(), DerNull().encode()]).encode(),
DerOctetString(hash.digest()).encode()
]).encode()
sig1 = p115.sign(hash)
@@ -1643,7 +1641,7 @@ class TestPKeyBackup(TestCaseLoggedIn):
flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_EXPORTABLE)
self.addCleanup(pkey.delete)
pkcs8_der, kek_der = kekek.export_pkey(pkey)
- kek = PKCS115_Cipher(PreloadedKey.db[HAL_KEY_TYPE_RSA_PRIVATE, 1024].obj).decrypt(
+ kek = PKCS1_v1_5.new(PreloadedKey.db[HAL_KEY_TYPE_RSA_PRIVATE, 1024].obj).decrypt(
self.parse_EncryptedPrivateKeyInfo(kek_der, self.oid_rsaEncryption),
self.make_kek())
der = AESKeyWrapWithPadding(kek).unwrap(
@@ -1662,7 +1660,7 @@ class TestPKeyBackup(TestCaseLoggedIn):
AESKeyWrapWithPadding(kek).wrap(der),
self.oid_aesKeyWrap),
kek = self.encode_EncryptedPrivateKeyInfo(
- PKCS115_Cipher(RSA.importKey(kekek.public_key)).encrypt(kek),
+ PKCS1_v1_5.new(RSA.importKey(kekek.public_key)).encrypt(kek),
self.oid_rsaEncryption),
flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)
self.addCleanup(pkey.delete)
@@ -1800,10 +1798,10 @@ class PreloadedRSAKey(PreloadedKey):
k2, k2.exportKey(format = "DER" ), keylen = keylen)
def sign(self, text, hash):
- return PKCS115_SigScheme(self.obj).sign(hash(text))
+ return pkcs1_15.new(self.obj).sign(hash(text))
def verify(self, text, hash, signature):
- return PKCS115_SigScheme(self.obj).verify(hash(text), signature)
+ return pkcs1_15.new(self.obj).verify(hash(text), signature)
class PreloadedECKey(PreloadedKey):