laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/35633?usp=email )
Change subject: New pySim.esim.x509_cert module for X.509 certificate handling
......................................................................
New pySim.esim.x509_cert module for X.509 certificate handling
Change-Id: Ia8cc2dac02fcd96624dc6d9348f103373eeeb614
---
A pySim/esim/x509_cert.py
1 file changed, 143 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/33/35633/1
diff --git a/pySim/esim/x509_cert.py b/pySim/esim/x509_cert.py
new file mode 100644
index 0000000..a42b025
--- /dev/null
+++ b/pySim/esim/x509_cert.py
@@ -0,0 +1,134 @@
+# Implementation of X.509 certificate handling in GSMA eSIM
+# as per SGP22 v3.0
+#
+# (C) 2024 by Harald Welte <laforge(a)osmocom.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import requests
+from typing import Optional, List
+
+from cryptography.hazmat.primitives.asymmetric import ec, padding
+from cryptography.hazmat.primitives import hashes
+from cryptography.exceptions import InvalidSignature
+from cryptography import x509
+
+from pySim.utils import b2h
+
+def check_signed(signed: x509.Certificate, signer: x509.Certificate) -> bool:
+ """Verify if 'signed' certificate was signed using 'signer'."""
+ # this code only works for ECDSA, but this is all we need for GSMA eSIM
+ pkey = signer.public_key()
+ # this 'signed.signature_algorithm_parameters' below requires cryptopgraphy 41.0.0 :(
+ pkey.verify(signed.signature, signed.tbs_certificate_bytes, signed.signature_algorithm_parameters)
+
+def cert_get_subject_key_id(cert: x509.Certificate) -> bytes:
+ """Obtain the subject key identifier of the given cert object (as raw bytes)."""
+ ski_ext = cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
+ return ski_ext.key_identifier
+
+def cert_get_auth_key_id(cert: x509.Certificate) -> bytes:
+ """Obtain the authority key identifier of the given cert object (as raw bytes)."""
+ aki_ext = cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier).value
+ return aki_ext.key_identifier
+
+
+class CertificateSet:
+ """A set of certificates consisting of a trusted [self-signed] CA root certificate,
+ and an optional number of intermediate certificates. Can be used to verify the certificate chain
+ of any given other certificate."""
+ def __init__(self, root_cert: x509.Certificate):
+ check_signed(root_cert, root_cert)
+ # TODO: check other mandatory attributes for CA Cert
+ usage_ext = root_cert.extensions.get_extension_for_class(x509.KeyUsage).value
+ if not usage_ext.key_cert_sign:
+ raise ValueError('Given root certificate key usage does not permit signing of certificates')
+ if not usage_ext.crl_sign:
+ raise ValueError('Given root certificate key usage does not permit signing of CRLs')
+ self.root_cert = root_cert
+ self.intermediate_certs = {}
+ self.crl = None
+
+ def load_crl(self, urls: Optional[List[str]] = None):
+ if urls and type(urls) is str:
+ urls = [urls]
+ if not urls:
+ # generate list of CRL URLs from root CA certificate
+ crl_ext = self.root_cert.extensions.get_extension_for_class(x509.CRLDistributionPoints).value
+ name_list = [x.full_name for x in crl_ext]
+ merged_list = []
+ for n in name_list:
+ merged_list += n
+ uri_list = filter(lambda x: isinstance(x, x509.UniformResourceIdentifier), merged_list)
+ urls = [x.value for x in uri_list]
+
+ for url in urls:
+ try:
+ crl_bytes = requests.get(url)
+ except requests.exceptions.ConnectionError:
+ continue
+ crl = x509.load_der_x509_crl(crl_bytes)
+ if not crl.is_signature_valid(self.root_cert.public_key()):
+ raise ValueError('Given CRL has incorrect signature and cannot be trusted')
+ # FIXME: various other checks
+ self.crl = crl
+ # FIXME: should we support multiple CRLs? we only support a single CRL right now
+ return
+ # FIXME: report on success/failure
+
+ @property
+ def root_cert_id(self) -> bytes:
+ return cert_get_subject_key_id(self.root_cert)
+
+ def add_intermediate_cert(self, cert: x509.Certificate):
+ """Add a potential intermediate certificate to the CertificateSet."""
+ # TODO: check mandatory attributes for intermediate cert
+ usage_ext = cert.extensions.get_extension_for_class(x509.KeyUsage).value
+ if not usage_ext.key_cert_sign:
+ raise ValueError('Given intermediate certificate key usage does not permit signing of certificates')
+ aki = cert_get_auth_key_id(cert)
+ ski = cert_get_subject_key_id(cert)
+ if aki == ski:
+ raise ValueError('Cannot add self-signed cert as intermediate cert')
+ self.intermediate_certs[ski] = cert
+ # TODO: we could test if this cert verifies against the root, and mark it as pre-verified
+ # so we don't need to verify again and again the chain of intermediate certificates
+
+ def verify_cert_crl(self, cert: x509.Certificate):
+ if not self.crl:
+ # we cannot check if there's no CRL
+ return
+ if self.crl.get_revoked_certificate_by_serial_number(cert.serial_nr):
+ raise VerifyError('Certificate is present in CRL, verification failed')
+
+ def verify_cert_chain(self, cert: x509.Certificate, max_depth: int = 100):
+ """Verify if a given certificate's signature chain can be traced back to the root CA of this
+ CertificateSet."""
+ depth = 1
+ c = cert
+ while True:
+ aki = cert_get_auth_key_id(c)
+ if aki == self.root_cert_id:
+ # last step:
+ check_signed(c, self.root_cert)
+ return
+ parent_cert = self.intermediate_certs.get(aki, None)
+ if not aki:
+ raise VerifyError('Could not find intermediate certificate for AuthKeyId %s' % b2h(aki))
+ check_signed(c, parent_cert)
+ # if we reach here, we passed (no exception raised)
+ c = parent_cert
+ depth += 1
+ if depth > max_depth:
+ raise VerifyError('Maximum depth %u exceeded while verifying certificate chain' % max_depth)
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35633?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ia8cc2dac02fcd96624dc6d9348f103373eeeb614
Gerrit-Change-Number: 35633
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/35635?usp=email )
Change subject: osmo-smdpp: Implement eUICC + EUM certificate signatre chain validation
......................................................................
osmo-smdpp: Implement eUICC + EUM certificate signatre chain validation
Change-Id: I961827c50ed5e34c6507bfdf853952ece5b0d121
---
M osmo-smdpp.py
M pySim/esim/rsp.py
2 files changed, 42 insertions(+), 20 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/35/35635/1
diff --git a/osmo-smdpp.py b/osmo-smdpp.py
index 7e0db27..2d1cd66 100755
--- a/osmo-smdpp.py
+++ b/osmo-smdpp.py
@@ -36,7 +36,8 @@
import pySim.esim.rsp as rsp
from pySim.esim.es8p import *
-from pySim.esim.x509_cert import oid, cert_policy_has_oid, CertAndPrivkey
+from pySim.esim.x509_cert import oid, cert_policy_has_oid, cert_get_auth_key_id
+from pySim.esim.x509_cert import CertAndPrivkey, CertificateSet, cert_get_subject_key_id
# HACK: make this configurable
DATA_DIR = './smdpp-data'
@@ -214,7 +215,11 @@
if 'euiccCiPKIdListForSigningV3' in euiccInfo1:
pkid_list = pkid_list + euiccInfo1['euiccCiPKIdListForSigningV3']
# verify it supports one of the keys indicated by euiccCiPKIdListForSigning
- if not any(self.ci_get_cert_for_pkid(x) for x in pkid_list):
+ for x in pkid_list:
+ ci_cert = self.ci_get_cert_for_pkid(x)
+ if ci_cert:
+ break
+ if not ci_cert:
raise ApiError('8.8.2', '3.1', 'None of the proposed Public Key Identifiers is supported by the SM-DP+')
# TODO: Determine the set of CERT.DPauth.SIG that satisfy the following criteria:
@@ -257,7 +262,8 @@
#output['otherCertsInChain'] = b64encode2str()
# create SessionState and store it in rss
- self.rss[transactionId] = rsp.RspSessionState(transactionId, serverChallenge)
+ self.rss[transactionId] = rsp.RspSessionState(transactionId, serverChallenge,
+ cert_get_subject_key_id(ci_cert))
return output
@@ -292,29 +298,35 @@
euicc_cert = x509.load_der_x509_certificate(euiccCertificate_bin)
eum_cert = x509.load_der_x509_certificate(eumCertificate_bin)
- # TODO: Verify the validity of the eUICC certificate chain
- # raise ApiError('8.1.3', '6.1', 'Verification failed')
- # raise ApiError('8.1.3', '6.3', 'Expired')
-
- # TODO: Verify that the Root Certificate of the eUICC certificate chain corresponds to the
- # euiccCiPKIdToBeUsed or euiccCiPKIdToBeUsedV3
- # raise ApiError('8.11.1', '3.9', 'Unknown')
-
- # Verify euiccSignature1 over euiccSigned1 using pubkey from euiccCertificate.
- # Otherwise, the SM-DP+ SHALL return a status code "eUICC - Verification failed"
- if not self._ecdsa_verify(euicc_cert, euiccSignature1_bin, euiccSigned1_bin):
- raise ApiError('8.1', '6.1', 'Verification failed')
-
# Verify that the transactionId is known and relates to an ongoing RSP session. Otherwise, the SM-DP+
# SHALL return a status code "TransactionId - Unknown"
ss = self.rss.get(transactionId, None)
if ss is None:
raise ApiError('8.10.1', '3.9', 'Unknown')
ss.euicc_cert = euicc_cert
- ss.eum_cert = eum_cert # do we need this in the state?
+ ss.eum_cert = eum_cert # TODO: do we need this in the state?
- # TODO: verify eUICC cert is signed by EUM cert
- # TODO: verify EUM cert is signed by CI cert
+ # Verify that the Root Certificate of the eUICC certificate chain corresponds to the
+ # euiccCiPKIdToBeUsed or TODO: euiccCiPKIdToBeUsedV3
+ if cert_get_auth_key_id(eum_cert) != ss.ci_cert_id:
+ raise ApiError('8.11.1', '3.9', 'Unknown')
+
+ # Verify the validity of the eUICC certificate chain
+ cs = CertificateSet(self.ci_get_cert_for_pkid(ss.ci_cert_id))
+ cs.add_intermediate_cert(eum_cert)
+ # TODO v3: otherCertsInChain
+ try:
+ cs.verify_cert_chain(euicc_cert)
+ except VerifyError:
+ raise ApiError('8.1.3', '6.1', 'Verification failed')
+ # raise ApiError('8.1.3', '6.3', 'Expired')
+
+
+ # Verify euiccSignature1 over euiccSigned1 using pubkey from euiccCertificate.
+ # Otherwise, the SM-DP+ SHALL return a status code "eUICC - Verification failed"
+ if not self._ecdsa_verify(euicc_cert, euiccSignature1_bin, euiccSigned1_bin):
+ raise ApiError('8.1', '6.1', 'Verification failed')
+
# TODO: verify EID of eUICC cert is within permitted range of EUM cert
ss.eid = ss.euicc_cert.subject.get_attributes_for_oid(x509.oid.NameOID.SERIAL_NUMBER)[0].value
diff --git a/pySim/esim/rsp.py b/pySim/esim/rsp.py
index b5289be..1f8e989 100644
--- a/pySim/esim/rsp.py
+++ b/pySim/esim/rsp.py
@@ -35,10 +35,11 @@
and subsequently used by further API calls using the same transactionId. The session state
is removed either after cancelSession or after notification.
TODO: add some kind of time based expiration / garbage collection."""
- def __init__(self, transactionId: str, serverChallenge: bytes):
+ def __init__(self, transactionId: str, serverChallenge: bytes, ci_cert_id: bytes):
self.transactionId = transactionId
self.serverChallenge = serverChallenge
# used at a later point between API calsl
+ self.ci_cert_id = ci_cert_id
self.euicc_cert: Optional[x509.Certificate] = None
self.eum_cert: Optional[x509.Certificate] = None
self.eid: Optional[bytes] = None
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35635?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I961827c50ed5e34c6507bfdf853952ece5b0d121
Gerrit-Change-Number: 35635
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-msc/+/35631?usp=email )
Change subject: vlr: Use new libosmogsm struct osmo_gsup_pdp_info fields
......................................................................
vlr: Use new libosmogsm struct osmo_gsup_pdp_info fields
This also makes sure it doesn't compile against older libosmogsm gsup
versions which would break ABI.
Related: OS#6091
Depends: libosmocore.git Change-Id 70be3560659c58f24b8db529c4fc85da4bb0ec04
Change-Id: Ia002fd6e0334d56de34d352a0bf1a8604e2e9fd3
---
M TODO-RELEASE
M src/libvlr/vlr.c
2 files changed, 22 insertions(+), 2 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-msc refs/changes/31/35631/1
diff --git a/TODO-RELEASE b/TODO-RELEASE
index d0852fc..8b07972 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,3 +7,4 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
+libosmogsm >1.9.0 ABI breakage in struct osmo_gsup_pdp_info, use new fields pdp_type_* and pdp_address.
\ No newline at end of file
diff --git a/src/libvlr/vlr.c b/src/libvlr/vlr.c
index fb034cf..e2e02bf 100644
--- a/src/libvlr/vlr.c
+++ b/src/libvlr/vlr.c
@@ -731,7 +731,9 @@
struct llist_head list;
unsigned int context_id;
- uint16_t pdp_type;
+ enum gsm48_pdp_type_org pdp_type_org;
+ enum gsm48_pdp_type_nr pdp_type_nr;
+ struct osmo_sockaddr pdp_address[2];
char apn_str[GSM_APN_LENGTH];
uint8_t qos_subscribed[20];
size_t qos_subscribed_len;
@@ -1024,7 +1026,10 @@
}
OSMO_ASSERT(pdp_data != NULL);
- pdp_data->pdp_type = pdp_info->pdp_type;
+ pdp_data->pdp_type_org = pdp_info->pdp_type_org;
+ pdp_data->pdp_type_nr = pdp_info->pdp_type_nr;
+ memcpy(&pdp_data->pdp_address[0], &pdp_info->pdp_address[0], sizeof(pdp_data->pdp_address[0]));
+ memcpy(&pdp_data->pdp_address[1], &pdp_info->pdp_address[1], sizeof(pdp_data->pdp_address[1]));
osmo_apn_to_str(pdp_data->apn_str,
pdp_info->apn_enc, pdp_info->apn_enc_len);
memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len);
--
To view, visit https://gerrit.osmocom.org/c/osmo-msc/+/35631?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-msc
Gerrit-Branch: master
Gerrit-Change-Id: Ia002fd6e0334d56de34d352a0bf1a8604e2e9fd3
Gerrit-Change-Number: 35631
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: newchange
Attention is currently required from: fixeria, laforge, lynxis lazus.
Hello Jenkins Builder, fixeria, laforge, lynxis lazus,
I'd like you to reexamine a change. Please visit
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/35629?usp=email
to look at the new patch set (#2).
The following approvals got outdated and were removed:
Verified+1 by Jenkins Builder
Change subject: GSUP: Convert PDP-Type IE to PDP-Address IE
......................................................................
GSUP: Convert PDP-Type IE to PDP-Address IE
The previous PDP-Type IE should have been a PDP-Address from the
start, since having only PDP-Type with no address is only a specific
case (dynamic addressing).
This becomes clear by looking at other similar protocols like:
* MAP: APN-Configuration IE has servedPartyIP-IP{v4,v6}-Address IEs
* Diameter S6b, 3GPP TS 29.272 7.3.35 APN-Configuration contains
Served-Party-IP-Address AVPs
* Diameter SWx, 3GPP TS 29.273 APN-Configuration.
* GTPv1C Ts 29.060 7.7.29 PDP Context containing PDP Address.
Since PDP-Type on its own really makes no sense, being it a special case
of PDP-Address, let's keep the IE by renaming it (keeping old name too
for API backward compat) and extend it to support lengths > 2 bytes.
Old implementation of libosmogsm gsup actually ignored lengths > 2
bytes, so we are safe acting against older implementations here, both
on the sending and receiving side on the wire.
Change-Id: I3e92368fff61694bcef6a48320595b59ae8f54ca
Related: OS#6091
Related: libosmocore.git Change-Id I775ff9c3be165d9f30d6ab55d03f99b6104eadd6
Related: osmo-gsm-manuals.git Change-Id I775ff9c3be165d9f30d6ab55d03f99b6104eadd6
---
M library/GSUP_Templates.ttcn
M library/GSUP_Types.ttcn
M sgsn/SGSN_Tests.ttcn
3 files changed, 91 insertions(+), 12 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/29/35629/2
--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/35629?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I3e92368fff61694bcef6a48320595b59ae8f54ca
Gerrit-Change-Number: 35629
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Attention: laforge <laforge(a)osmocom.org>
Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Attention: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-MessageType: newpatchset
Attention is currently required from: laforge.
pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-gsm-manuals/+/35622?usp=email )
Change subject: Update spec reference s/TS 04.08/TS 24.008/
......................................................................
Patch Set 1:
(1 comment)
Patchset:
PS1:
> not all parts of 04.08 went into 24.008, but I guess it is correct in this case.
Yes, I checked.
--
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-manuals/+/35622?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-gsm-manuals
Gerrit-Branch: master
Gerrit-Change-Id: Ie9feade73eff8c61232deffbad24ef34d97d32ac
Gerrit-Change-Number: 35622
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Attention: laforge <laforge(a)osmocom.org>
Gerrit-Comment-Date: Fri, 19 Jan 2024 19:53:28 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: comment