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>
342 lines
12 KiB
Diff
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
|
|
|