From 5bf5d6c22b9461321cec9ddeaae5795a5465bb03 Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Thu, 23 Sep 2021 21:34:03 +0200 Subject: [PATCH 4/6] tpm2_ptool: do not re-encode the signed data when importing a certificate When using `tpm2_ptool addcert`, several users experienced issues because the signed data of the certificate was re-encoded when being added to the database. More precisely, the encoded certificate data is encoded using a BER encoder which encodes booleans using 1 of True (cf. https://github.com/etingof/pyasn1/blob/v0.4.8/pyasn1/codec/ber/encoder.py#L164 ). But in DER, the encoding of "True" is 0xff, and changing the signed data made the signature of the certificate no longer valid. To fix this issue: - Directly use the result of `pem.readPemFromFile(f)` in attribute `CKA_VALUE`: this is directly the encoded form of the certificate. - Remove `pyasn1.codec.ber`, as this encoder is no longer used. - Rename the DER decoder from `decoder` to `derdecoder` and the encoder from `derenc` to `derencoder`, to make the code easier to read. While at it: - Reindent the code to 4-space indentation - Use `hashlib.sha1(bercert).digest()` directly to compute a SHA1 digest, instead of using `m.update()`. Fixes: https://github.com/tpm2-software/tpm2-pkcs11/issues/700 Signed-off-by: Nicolas Iooss --- tools/tpm2_pkcs11/utils.py | 126 ++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/tools/tpm2_pkcs11/utils.py b/tools/tpm2_pkcs11/utils.py index b803f4c..91eab9a 100644 --- a/tools/tpm2_pkcs11/utils.py +++ b/tools/tpm2_pkcs11/utils.py @@ -15,9 +15,7 @@ from cryptography.hazmat.primitives.asymmetric import (rsa, padding) from cryptography.hazmat.primitives import hashes from pyasn1_modules import pem, rfc2459 -from pyasn1.codec.der import decoder -from pyasn1.codec.ber import encoder as berenc -from pyasn1.codec.der import encoder as derenc +from pyasn1.codec.der import decoder as derdecoder, encoder as derencoder from pyasn1.type import namedtype, tag, univ from .pkcs11t import * # noqa @@ -247,68 +245,64 @@ def asn1_format_ec_point_uncompressed(x, y): return s def pemcert_to_attrs(certpath): - # rather than use pycryptography x509 parser, which gives native type access to certificate - # fields use pyASN1 to get raw ASN1 encoded values for the fields as the spec requires them - with open(certpath, "r") as f: - substrate = pem.readPemFromFile(f) - cert = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())[0] - - c = cert['tbsCertificate'] - - # print(cert.prettyPrint()) - - h = binascii.hexlify - b = berenc.encode - d = derenc.encode - - bercert = b(cert) - hexbercert = h(bercert).decode() - - # the CKA_CHECKSUM attrs is the first 3 bytes of a sha1hash - m = hashlib.sha1() - m.update(bercert) - bercertchecksum = m.digest()[0:3] - hexbercertchecksum = h(bercertchecksum).decode() - - subj = c['subject'] - hexsubj = h(d(str2bytes(subj))).decode() - - issuer = c['issuer'] - hexissuer = h(d(str2bytes(issuer))).decode() - - serial = c['serialNumber'] - hexserial = h(d(str2bytes(serial))).decode() - - return { - # The attrs of this attribute is derived by taking the first 3 bytes of the CKA_VALUE - # field. - CKA_CHECK_VALUE: hexbercertchecksum, - # Start date for the certificate (default empty) - CKA_START_DATE : "", - # End date for the certificate (default empty) - CKA_END_DATE : "", - # DER-encoding of the SubjectPublicKeyInfo for the public key - # contained in this certificate (default empty) - CKA_PUBLIC_KEY_INFO : "", - # DER encoded subject - CKA_SUBJECT : hexsubj, - # DER encoding of issuer - CKA_ISSUER : hexissuer, - # DER encoding of the cert serial - CKA_SERIAL_NUMBER : hexserial, - # BER encoding of the certificate - CKA_VALUE : hexbercert, - # RFC2279 string to URL where cert can be found, default empty - CKA_URL : '', - # hash of pub key subj, default empty - CKA_HASH_OF_SUBJECT_PUBLIC_KEY : '', - # Hash of pub key, default empty - CKA_HASH_OF_ISSUER_PUBLIC_KEY : '', - # Java security domain, default CK_SECURITY_DOMAIN_UNSPECIFIED - CKA_JAVA_MIDP_SECURITY_DOMAIN : CK_SECURITY_DOMAIN_UNSPECIFIED, - # Name hash algorithm, defaults to SHA1 - CKA_NAME_HASH_ALGORITHM : CKM_SHA_1 - } + # rather than using pycryptography x509 parser, which gives native type access to certificate + # fields use pyASN1 to get raw ASN1 encoded values for the fields as the spec requires them + with open(certpath, "r") as f: + bercert = pem.readPemFromFile(f) + + cert = derdecoder.decode(bercert, asn1Spec=rfc2459.Certificate())[0] + c = cert['tbsCertificate'] + + # print(cert.prettyPrint()) + + h = binascii.hexlify + d = derencoder.encode + + hexbercert = h(bercert).decode() + + # the CKA_CHECKSUM attrs is the first 3 bytes of a sha1hash + bercertchecksum = hashlib.sha1(bercert).digest()[0:3] + hexbercertchecksum = h(bercertchecksum).decode() + + subj = c['subject'] + hexsubj = h(d(str2bytes(subj))).decode() + + issuer = c['issuer'] + hexissuer = h(d(str2bytes(issuer))).decode() + + serial = c['serialNumber'] + hexserial = h(d(str2bytes(serial))).decode() + + return { + # The attrs of this attribute is derived by taking the first 3 bytes of the CKA_VALUE + # field. + CKA_CHECK_VALUE: hexbercertchecksum, + # Start date for the certificate (default empty) + CKA_START_DATE: "", + # End date for the certificate (default empty) + CKA_END_DATE: "", + # DER-encoding of the SubjectPublicKeyInfo for the public key + # contained in this certificate (default empty) + CKA_PUBLIC_KEY_INFO: "", + # DER encoded subject + CKA_SUBJECT: hexsubj, + # DER encoding of issuer + CKA_ISSUER: hexissuer, + # DER encoding of the cert serial + CKA_SERIAL_NUMBER: hexserial, + # BER encoding of the certificate + CKA_VALUE: hexbercert, + # RFC2279 string to URL where cert can be found, default empty + CKA_URL: '', + # hash of pub key subj, default empty + CKA_HASH_OF_SUBJECT_PUBLIC_KEY: '', + # Hash of pub key, default empty + CKA_HASH_OF_ISSUER_PUBLIC_KEY: '', + # Java security domain, default CK_SECURITY_DOMAIN_UNSPECIFIED + CKA_JAVA_MIDP_SECURITY_DOMAIN: CK_SECURITY_DOMAIN_UNSPECIFIED, + # Name hash algorithm, defaults to SHA1 + CKA_NAME_HASH_ALGORITHM: CKM_SHA_1 + } def _pkcs11_to_str(value, prefix): @@ -407,7 +401,7 @@ def asn1parse_tss_key(keypath): if len(substrate) == 0: sys.exit('Did not find key in tss key file: {}'.format(keypath)) - tss2_privkey, _ = decoder.decode(substrate, asn1Spec=TSSPrivKey()) + tss2_privkey, _ = derdecoder.decode(substrate, asn1Spec=TSSPrivKey()) return tss2_privkey -- 2.38.1