linux-sgx/0121-pccsadmin-convert-from-asn1-to-pyasn1-python-module.patch
Daniel P. Berrangé 12589a1af6 Port to pycryptography and pyasn1 and make keyring optional
pyOpenSSL 24.0.0 removed several APIs required by pccsadmin, so
porting to pycryptography is required on Fedora. Since RHEL does
not ship pyOpenSSL, the port is useful here too.

Using pyasn1 instead of asn1 gives stronger validation during
parsing and brings compatibility with RHEL that lacks python3-asn1

The keyring package needs to be optional on RHEL which lacks this
module (currently).

Also drop the inappropriate pccs port number change

Related: https://issues.redhat.com/browse/RHEL-121612
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2025-12-10 11:17:54 +00:00

342 lines
12 KiB
Diff

From b9954581944446455876728bdab816090d773715 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
Date: Thu, 4 Dec 2025 13:54:19 +0000
Subject: [PATCH 121/126] pccsadmin: convert from asn1 to pyasn1 python module
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The pyasn1 module decodes using a formal object model so is more robust,
as well as being more widely available in distros.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
tools/PccsAdminTool/lib/intelsgx/pckcert.py | 267 +++++++++++++-------
1 file changed, 177 insertions(+), 90 deletions(-)
diff --git a/tools/PccsAdminTool/lib/intelsgx/pckcert.py b/tools/PccsAdminTool/lib/intelsgx/pckcert.py
index 97aa278..eaed331 100644
--- a/tools/PccsAdminTool/lib/intelsgx/pckcert.py
+++ b/tools/PccsAdminTool/lib/intelsgx/pckcert.py
@@ -1,76 +1,171 @@
from cryptography import x509
from cryptography.x509.oid import ObjectIdentifier
from cryptography.hazmat.backends import default_backend
-import asn1
-import struct
+import pyasn1
+from pyasn1.codec.der.decoder import decode as der_decoder
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import opentype
+from pyasn1.type import univ
+
+
+id_cdp_extensionStr = '2.5.29.31'
+id_ce_sGXExtensionsStr = '1.2.840.113741.1.13.1'
+
+id_ce_sGXExtensions = univ.ObjectIdentifier(id_ce_sGXExtensionsStr)
+
+id_ce_sGXExtensions_pPID = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".1")
+id_ce_sGXExtensions_tCB = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2")
+id_ce_sGXExtensions_pCE_ID = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".3")
+id_ce_sGXExtensions_fMSPC = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".4")
+id_ce_sGXExtensions_sGXType = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".5")
+id_ce_sGXExtensions_platformInstanceID = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".6")
+id_ce_sGXExtensions_configuration = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".7")
+
+id_ce_tCB_sGXTCBComp01SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.1")
+id_ce_tCB_sGXTCBComp02SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.2")
+id_ce_tCB_sGXTCBComp03SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.3")
+id_ce_tCB_sGXTCBComp04SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.4")
+id_ce_tCB_sGXTCBComp05SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.5")
+id_ce_tCB_sGXTCBComp06SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.6")
+id_ce_tCB_sGXTCBComp07SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.7")
+id_ce_tCB_sGXTCBComp08SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.8")
+id_ce_tCB_sGXTCBComp09SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.9")
+id_ce_tCB_sGXTCBComp10SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.10")
+id_ce_tCB_sGXTCBComp11SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.11")
+id_ce_tCB_sGXTCBComp12SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.12")
+id_ce_tCB_sGXTCBComp13SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.13")
+id_ce_tCB_sGXTCBComp14SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.14")
+id_ce_tCB_sGXTCBComp15SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.15")
+id_ce_tCB_sGXTCBComp16SVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.16")
+id_ce_tCB_pCESVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.17")
+id_ce_tCB_cPUSVN = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".2.18")
+
+id_ce_configuration_dynamicPlatform = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".7.1")
+id_ce_configuration_cachedKeys = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".7.2")
+id_ce_configuration_sMTEnabled = univ.ObjectIdentifier(id_ce_sGXExtensionsStr + ".7.3")
+
+
+class SgxExtensionPPID(univ.OctetString):
+ pass
+
+
+class SgxCPUSVN(univ.OctetString):
+ pass
+
+
+tcbAttributeMap = {
+ id_ce_tCB_sGXTCBComp01SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp02SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp03SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp04SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp05SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp06SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp07SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp08SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp09SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp10SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp11SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp12SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp13SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp14SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp15SVN: univ.Integer(),
+ id_ce_tCB_sGXTCBComp16SVN: univ.Integer(),
+ id_ce_tCB_pCESVN: univ.Integer(),
+ id_ce_tCB_cPUSVN: SgxCPUSVN(),
+}
+
+
+class SgxExtensionTCBEntry(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('tCBId', univ.ObjectIdentifier()),
+ namedtype.NamedType('tCBValue', univ.Any(),
+ openType=opentype.OpenType('tCBId',
+ tcbAttributeMap))
+ )
+
+
+class SgxExtensionTCB(univ.SequenceOf):
+ componentType = SgxExtensionTCBEntry()
+
+
+class SgxExtensionPCEID(univ.OctetString):
+ pass
+
+
+class SgxExtensionFMSPC(univ.OctetString):
+ pass
+
+
+class SgxExtensionSGXType(univ.Enumerated):
+ namedValues = namedval.NamedValues(
+ ('standard', 0),
+ ('scalable', 1),
+ ('scalableWithIntegrity', 2)
+ )
+
+
+class SgxExtensionPlatformInstanceID(univ.OctetString):
+ pass
+
+
+configurationAttributeMap = {
+ id_ce_configuration_dynamicPlatform: univ.Boolean(),
+ id_ce_configuration_cachedKeys: univ.Boolean(),
+ id_ce_configuration_sMTEnabled: univ.Boolean(),
+}
+
+
+class SgxExtensionConfigurationEntry(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('configurationId', univ.ObjectIdentifier()),
+ namedtype.NamedType('configurationValue', univ.Any(),
+ openType=opentype.OpenType('configurationId',
+ configurationAttributeMap))
+ )
+
+
+class SgxExtensionConfiguration(univ.SequenceOf):
+ componentType = SgxExtensionConfigurationEntry()
+
+
+extensionAttributeMap = {
+ id_ce_sGXExtensions_pPID: SgxExtensionPPID(),
+ id_ce_sGXExtensions_tCB: SgxExtensionTCB(),
+ id_ce_sGXExtensions_pCE_ID: SgxExtensionPCEID(),
+ id_ce_sGXExtensions_fMSPC: SgxExtensionFMSPC(),
+ id_ce_sGXExtensions_sGXType: SgxExtensionSGXType(),
+ id_ce_sGXExtensions_platformInstanceID: SgxExtensionPlatformInstanceID(),
+ id_ce_sGXExtensions_configuration: SgxExtensionConfiguration(),
+}
+
+
+class SgxExtensionEntry(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('sGXExtensionId', univ.ObjectIdentifier()),
+ namedtype.NamedType('sGXExtensionValue', univ.Any(),
+ openType=opentype.OpenType('sGXExtensionId',
+ extensionAttributeMap))
+ )
+
+
+class SgxExtension(univ.SequenceOf):
+ componentType = SgxExtensionEntry()
-# This is a very simplistic ASN1 parser. Production code should use
-# something like ans1c to build a parser from the ASN1 spec file so
-# that it can check and enforce data validity.
class SgxPckCertificateExtensions:
- id_ce_sGXExtensions = '1.2.840.113741.1.13.1'
- id_ce_sGXExtensions_tCB= id_ce_sGXExtensions+".2"
- id_ce_sGXExtensions_configuration= id_ce_sGXExtensions+".7"
- id_cdp_extension = '2.5.29.31'
- decoder= asn1.Decoder()
- _data= {}
- ca= ''
- oids= {
- id_ce_sGXExtensions: 'sGXExtensions',
- id_ce_sGXExtensions+".1": 'pPID',
- id_ce_sGXExtensions_tCB: 'tCB',
- id_ce_sGXExtensions_tCB+".1": 'tCB-sGXTCBComp01SVN',
- id_ce_sGXExtensions_tCB+".2": 'tCB-sGXTCBComp02SVN',
- id_ce_sGXExtensions_tCB+".3": 'tCB-sGXTCBComp03SVN',
- id_ce_sGXExtensions_tCB+".4": 'tCB-sGXTCBComp04SVN',
- id_ce_sGXExtensions_tCB+".5": 'tCB-sGXTCBComp05SVN',
- id_ce_sGXExtensions_tCB+".6": 'tCB-sGXTCBComp06SVN',
- id_ce_sGXExtensions_tCB+".7": 'tCB-sGXTCBComp07SVN',
- id_ce_sGXExtensions_tCB+".8": 'tCB-sGXTCBComp08SVN',
- id_ce_sGXExtensions_tCB+".9": 'tCB-sGXTCBComp09SVN',
- id_ce_sGXExtensions_tCB+".10": 'tCB-sGXTCBComp10SVN',
- id_ce_sGXExtensions_tCB+".11": 'tCB-sGXTCBComp11SVN',
- id_ce_sGXExtensions_tCB+".12": 'tCB-sGXTCBComp12SVN',
- id_ce_sGXExtensions_tCB+".13": 'tCB-sGXTCBComp13SVN',
- id_ce_sGXExtensions_tCB+".14": 'tCB-sGXTCBComp14SVN',
- id_ce_sGXExtensions_tCB+".15": 'tCB-sGXTCBComp15SVN',
- id_ce_sGXExtensions_tCB+".16": 'tCB-sGXTCBComp16SVN',
- id_ce_sGXExtensions_tCB+".17": 'tCB-pCESVN',
- id_ce_sGXExtensions_tCB+".18": 'tCB-cPUSVN',
- id_ce_sGXExtensions+".3": 'pCE-ID',
- id_ce_sGXExtensions+".4": 'fMSPC',
- id_ce_sGXExtensions+".5": 'sGXType',
- id_ce_sGXExtensions+".6": 'platformInstanceID',
- id_ce_sGXExtensions_configuration: 'configuration',
- id_ce_sGXExtensions_configuration+".1": 'dynamicPlatform',
- id_ce_sGXExtensions_configuration+".2": 'cachedKeys',
- id_ce_sGXExtensions_configuration+".3": 'sMTEnabled'
- }
-
- def _parse_asn1(self, d, oid, lnr=asn1.Numbers.ObjectIdentifier):
- tag= self.decoder.peek()
- while tag:
- if tag.typ == asn1.Types.Constructed:
- self.decoder.enter()
- if ( lnr == asn1.Numbers.ObjectIdentifier ):
- d[self.oids[oid]]= {}
- self._parse_asn1(d[self.oids[oid]], oid, tag.nr)
- else:
- self._parse_asn1(d, oid, tag.nr)
- self.decoder.leave()
- elif tag.typ == asn1.Types.Primitive:
- tag, value= self.decoder.read()
- if ( tag.nr == asn1.Numbers.ObjectIdentifier ):
- oid= value
- else:
- d[self.oids[oid]]= value
- lnr= tag.nr
- tag= self.decoder.peek()
- return
+
+ def __init__(self):
+ self.ca= ''
+ self._data= None
+
+ def _parse_asn1(self, extensionData):
+ parsed, extra= der_decoder(extensionData,
+ asn1Spec=SgxExtension(),
+ decodeOpenTypes=True)
+ return parsed
def parse_pem_certificate(self, pem):
- self._data= {}
cert= x509.load_pem_x509_certificate(pem, default_backend())
issuerCN = cert.issuer.rfc4514_string()
if (issuerCN.find('Processor') != -1) :
@@ -81,63 +176,55 @@ class SgxPckCertificateExtensions:
self.ca = None
sgxext= cert.extensions.get_extension_for_oid(
- ObjectIdentifier(self.id_ce_sGXExtensions)
+ ObjectIdentifier(id_ce_sGXExtensionsStr)
)
- self.decoder.start(sgxext.value.value)
- self._parse_asn1(self._data, self.id_ce_sGXExtensions)
+ self._data= self._parse_asn1(sgxext.value.value)
def get_root_ca_crl(self, pem):
- self._data= {}
cert= x509.load_pem_x509_certificate(pem, default_backend())
cdpext= cert.extensions.get_extension_for_oid(
- ObjectIdentifier(self.id_cdp_extension)
+ ObjectIdentifier(id_cdp_extensionStr)
)
return getattr(getattr(cdpext.value[0], "_full_name")[0], "value")
- def data(self, field=None):
- if 'sGXExtensions' not in self._data:
- return None
-
- d= self._data['sGXExtensions']
-
- if field:
- if field in d:
- return d[field]
+ def data(self, field):
+ if self._data is None:
return None
- return d
+ ent = list(filter(lambda e: e['sGXExtensionId'] == field, self._data))[0]
+ return ent['sGXExtensionValue']
def _hex_data(self, field):
val= self.data(field)
if val is None:
return None
- return val.hex()
+ return bytes(val).hex()
# Commonly-needed data fields
#------------------------------
def get_fmspc(self):
- return self._hex_data('fMSPC')
+ return self._hex_data(id_ce_sGXExtensions_fMSPC)
def get_ca(self):
return self.ca
def get_tcbm(self):
- tcb= self.data('tCB')
+ tcb= self.data(id_ce_sGXExtensions_tCB)
if tcb is None:
return None
- return tcb['tCB-cPUSVN'].hex() + self.get_pcesvn()
+ ent= list(filter(lambda e: e['tCBId'] == id_ce_tCB_cPUSVN, tcb))[0]
+ return bytes(ent["tCBValue"]).hex() + self.get_pcesvn()
def get_pceid(self):
- return self._hex_data('pCE-ID')
+ return self._hex_data(id_ce_sGXExtensions_pCE_ID)
def get_ppid(self):
- return self._hex_data('pPID')
+ return self._hex_data(id_ce_sGXExtensions_pPID)
def get_pcesvn(self):
- tcb= self.data('tCB')
- # pCESVN should be packed little-endian
- pcesvn= struct.pack('<H', tcb['tCB-pCESVN'])
- return pcesvn.hex()
+ tcb= self.data(id_ce_sGXExtensions_tCB)
+ ent= list(filter(lambda e: e['tCBId'] == id_ce_tCB_pCESVN, tcb))[0]
+ return int(ent["tCBValue"]).to_bytes(2, byteorder='little').hex()
--
2.51.1