From e0d839942e00e20af4a086bb78d9f40ec5d3ca61 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 3 Oct 2024 17:00:15 +0200 Subject: [PATCH 1/8] feat: generate and parse libp2p tls certificate --- .pinned | 1 + libp2p.nimble | 3 +- libp2p/transports/tls/certificate.nim | 483 +++++++++++++++++++++++ tests/transports/tls/testcertificate.nim | 21 + 4 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 libp2p/transports/tls/certificate.nim create mode 100644 tests/transports/tls/testcertificate.nim diff --git a/.pinned b/.pinned index d996ac9cd9..8fe07ad0a9 100644 --- a/.pinned +++ b/.pinned @@ -5,6 +5,7 @@ dnsclient;https://github.com/ba0f3/dnsclient.nim@#23214235d4784d24aceed99bbfe153 faststreams;https://github.com/status-im/nim-faststreams@#720fc5e5c8e428d9d0af618e1e27c44b42350309 httputils;https://github.com/status-im/nim-http-utils@#3b491a40c60aad9e8d3407443f46f62511e63b18 json_serialization;https://github.com/status-im/nim-json-serialization@#85b7ea093cb85ee4f433a617b97571bd709d30df +mbedtls;https://github.com/status-im/nim-mbedtls.git@#740fb2f469511adc1772c5cb32395f4076b9e0c5 metrics;https://github.com/status-im/nim-metrics@#6142e433fc8ea9b73379770a788017ac528d46ff ngtcp2;https://github.com/status-im/nim-ngtcp2@#6834f4756b6af58356ac9c4fef3d71db3c3ae5fe nimcrypto;https://github.com/cheatfate/nimcrypto@#1c8d6e3caf3abc572136ae9a1da81730c4eb4288 diff --git a/libp2p.nimble b/libp2p.nimble index fddad59a8b..5873b8a7f4 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -11,7 +11,8 @@ requires "nim >= 1.6.0", "nimcrypto >= 0.6.0 & < 0.7.0", "dnsclient >= 0.3.0 & < 0.4.0", "bearssl >= 0.2.5", "chronicles >= 0.10.2", "chronos >= 4.0.3", "metrics", "secp256k1", "stew#head", "websock", "unittest2", - "https://github.com/status-im/nim-quic.git#ddcb31ffb74b5460ab37fd13547eca90594248bc" + "https://github.com/status-im/nim-quic.git#ddcb31ffb74b5460ab37fd13547eca90594248bc", + "https://github.com/status-im/nim-mbedtls.git" let nimc = getEnv("NIMC", "nim") # Which nim compiler to use let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim new file mode 100644 index 0000000000..339f73ddc6 --- /dev/null +++ b/libp2p/transports/tls/certificate.nim @@ -0,0 +1,483 @@ +# Nim-LibP2P +# Copyright (c) 2024 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +import std/[sequtils, strutils] + +import stew/byteutils +import chronicles + +import mbedtls/pk +import mbedtls/ctr_drbg +import mbedtls/entropy +import mbedtls/ecp +import mbedtls/sha256 +import mbedtls/md +import mbedtls/asn1 +import mbedtls/asn1write +import mbedtls/x509 +import mbedtls/x509_crt +import mbedtls/oid +import mbedtls/debug +import mbedtls/error +import nimcrypto/utils +import ../../crypto/crypto + +logScope: + topics = "libp2p tls certificate" + +# Constants and OIDs +const + P2P_SIGNING_PREFIX = "libp2p-tls-handshake:" + SIGNATURE_ALG = MBEDTLS_MD_SHA256 + EC_GROUP_ID = MBEDTLS_ECP_DP_SECP256R1 + LIBP2P_EXT_OID_DER: array[10, byte] = [ + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0xA2, 0x5A, 0x01, 0x01 + ] # "1.3.6.1.4.1.53594.1.1" + +# Exception types for TLS certificate errors +type + TLSCertificateError* = object of Exception + ASN1EncodingError* = object of TLSCertificateError + KeyGenerationError* = object of TLSCertificateError + CertificateCreationError* = object of TLSCertificateError + CertificateParsingError* = object of TLSCertificateError + IdentityPubKeySerializationError* = object of TLSCertificateError + IdentitySigningError* = object of TLSCertificateError + +# Define the P2pExtension and P2pCertificate types +type + P2pExtension* = object + publicKey*: seq[byte] + signature*: seq[byte] + + P2pCertificate* = object + certificate*: mbedtls_x509_crt + extension*: P2pExtension + +proc ptrInc*(p: ptr byte, n: uint): ptr byte = + ## Utility function to increment a pointer by n bytes. + cast[ptr byte](cast[uint](p) + n) + +proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.raises: [ASN1EncodingError].} = + ## Generates the ASN.1-encoded SignedKey structure. + ## + ## The SignedKey structure contains the public key and its signature, + ## encoded as a SEQUENCE of two OCTET STRINGs. + ## + ## Parameters: + ## - `signature`: The signature bytes. + ## - `pubKey`: The public key bytes. + ## + ## Returns: + ## A sequence of bytes representing the ASN.1-encoded SignedKey. + ## + ## Raises: + ## - `ASN1EncodingError` if ASN.1 encoding fails. + const + extValueSize = 256 # Buffer size for ASN.1 encoding + var + extValue: array[extValueSize, byte] + extPtr: ptr byte = addr extValue[extValueSize - 1] # Start at the end of the buffer + startPtr: ptr byte = addr extValue[0] + len = 0 + + # Write signature OCTET STRING + let retSig = mbedtls_asn1_write_octet_string( + addr extPtr, + startPtr, + unsafeAddr signature[0], + signature.len.uint + ) + if retSig < 0: + raise newException(ASN1EncodingError, "Failed to write signature OCTET STRING") + len += retSig + + # Write publicKey OCTET STRING + let retPub = mbedtls_asn1_write_octet_string( + addr extPtr, + startPtr, + unsafeAddr pubKey[0], + pubKey.len.uint + ) + if retPub < 0: + raise newException(ASN1EncodingError, "Failed to write publicKey OCTET STRING") + len += retPub + + # Total length of the SEQUENCE contents + let contentLen = retSig + retPub + # Write SEQUENCE length + let retLen = mbedtls_asn1_write_len( + addr extPtr, + startPtr, + contentLen.uint + ) + if retLen < 0: + raise newException(ASN1EncodingError, "Failed to write SEQUENCE length") + len += retLen + + # Write SEQUENCE tag + let retTag = mbedtls_asn1_write_tag( + addr extPtr, + startPtr, + MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE + ) + if retTag < 0: + raise newException(ASN1EncodingError, "Failed to write SEQUENCE tag") + len += retTag + + # Calculate dataOffset based on the accumulated length + let dataOffset = extValueSize - len - 1 + + # Extract the relevant portion of extValue as a seq[byte] + let extValueSeq = toSeq(extValue[dataOffset ..< extValueSize]) + + # Return the extension content + return extValueSeq + +proc makeLibp2pExtension( + identityKeypair: KeyPair, certificateKeypair: mbedtls_pk_context +): seq[byte] {.raises: [CertificateCreationError, IdentityPubKeySerializationError, IdentitySigningError, ASN1EncodingError, TLSCertificateError].} = + ## Creates the libp2p extension containing the SignedKey. + ## + ## The libp2p extension is an ASN.1-encoded structure that includes + ## the public key and its signature over the certificate's public key. + ## + ## Parameters: + ## - `identityKeypair`: The peer's identity key pair. + ## - `certificateKeypair`: The key pair used for the certificate. + ## + ## Returns: + ## A sequence of bytes representing the libp2p extension. + ## + ## Raises: + ## - `CertificateCreationError` if public key serialization fails. + ## - `IdentityPubKeySerializationError` if serialization of identity public key fails. + ## - `IdentitySigningError` if signing the hash fails. + ## - `ASN1EncodingError` if ASN.1 encoding fails. + + # Serialize the Certificate's Public Key + var + certPubKeyDer: array[512, byte] + certPubKeyDerLen: cint + + certPubKeyDerLen = mbedtls_pk_write_pubkey_der( + unsafeAddr certificateKeypair, + addr certPubKeyDer[0], + certPubKeyDer.len.uint + ) + if certPubKeyDerLen < 0: + raise newException(CertificateCreationError, "Failed to write certificate public key in DER format") + + # Adjust pointer to the start of the data + let certPubKeyDerPtr = + addr certPubKeyDer[certPubKeyDer.len - certPubKeyDerLen] + + # Create the Message to Sign + var msg = newSeq[byte](P2P_SIGNING_PREFIX.len + certPubKeyDerLen.int.int) + + # Copy the prefix into msg + for i in 0 ..< P2P_SIGNING_PREFIX.len: + msg[i] = byte(P2P_SIGNING_PREFIX[i]) + + # Copy the public key DER into msg + copyMem( + addr msg[P2P_SIGNING_PREFIX.len], + certPubKeyDerPtr, + certPubKeyDerLen.int + ) + + # Compute SHA-256 hash of the message + var hash: array[32, byte] + let hashRet = mbedtls_sha256( + msg[0].addr, msg.len.uint, addr hash[0], 0 # 0 for SHA-256 + ) + if hashRet != 0: + # Since hashing failure is critical and unlikely, we can raise a general exception + raise newException(TLSCertificateError, "Failed to compute SHA-256 hash") + + # Sign the hash with the Identity Key + let signatureResult = identityKeypair.seckey.sign(hash) + if signatureResult.isErr: + raise newException(IdentitySigningError, "Failed to sign the hash with the identity key") + let signature = signatureResult.get().data + + # Get the public key bytes + let pubKeyBytesResult = identityKeypair.pubkey.getBytes() + if pubKeyBytesResult.isErr: + raise newException(IdentityPubKeySerializationError, "Failed to get identity public key bytes") + let pubKeyBytes = pubKeyBytesResult.get() + + # Generate the SignedKey ASN.1 structure + return generateSignedKey(signature, pubKeyBytes) + +proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) + {.raises: [KeyGenerationError, CertificateCreationError, ASN1EncodingError, IdentityPubKeySerializationError, IdentitySigningError, TLSCertificateError].} = + ## Generates a self-signed X.509 certificate with the libp2p extension. + ## + ## Parameters: + ## - `identityKeyPair`: The peer's identity key pair. + ## + ## Returns: + ## A tuple containing: + ## - The DER-encoded certificate. + ## - The DER-encoded private key. + ## + ## Raises: + ## - `KeyGenerationError` if key generation fails. + ## - `CertificateCreationError` if certificate creation fails. + ## - `ASN1EncodingError` if encoding fails. + # Initialize entropy and DRBG contexts + var + entropy = mbedtls_entropy_context() + ctrDrbg = mbedtls_ctr_drbg_context() + crt: mbedtls_x509write_cert + certKey: mbedtls_pk_context + ret: cint + + mbedtls_entropy_init(addr entropy) + mbedtls_ctr_drbg_init(addr ctrDrbg) + mbedtls_x509write_crt_init(addr crt) + mbedtls_pk_init(addr certKey) + + defer: + mbedtls_entropy_free(addr entropy) + mbedtls_ctr_drbg_free(addr ctrDrbg) + mbedtls_pk_free(addr certKey) + mbedtls_x509write_crt_free(addr crt) + + # Seed the random number generator + let personalization = "libp2p_tls" + ret = mbedtls_ctr_drbg_seed( + addr ctrDrbg, + mbedtls_entropy_func, + addr entropy, + cast[ptr byte](personalization.cstring), + personalization.len.uint + ) + if ret != 0: + raise newException(KeyGenerationError, "Failed to seed CTR_DRBG") + + # Initialize certificate key + ret = mbedtls_pk_setup( + addr certKey, + mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY) + ) + if ret != 0: + raise newException(KeyGenerationError, "Failed to set up certificate key context") + + # Generate key pair for the certificate + let G = try: + mb_pk_ec(certKey) + except MbedTLSError as e: + raise newException(KeyGenerationError, e.msg) + ret = mbedtls_ecp_gen_key( + EC_GROUP_ID, G, mbedtls_ctr_drbg_random, addr ctrDrbg + ) + if ret != 0: + raise newException(KeyGenerationError, "Failed to generate EC key pair for certificate") + + ## Initialize libp2p extension + let libp2pExtension = makeLibp2pExtension(identityKeyPair, certKey) + + # Set the Subject and Issuer Name (self-signed) + ret = mbedtls_x509write_crt_set_subject_name(addr crt, "CN=libp2p.io") + if ret != 0: + raise newException(CertificateCreationError, "Failed to set subject name") + + ret = mbedtls_x509write_crt_set_issuer_name(addr crt, "CN=libp2p.io") + if ret != 0: + raise newException(CertificateCreationError, "Failed to set issuer name") + + # Set Validity Period + let notBefore = "19750101000000" + let notAfter = "40960101000000" + ret = mbedtls_x509write_crt_set_validity( + addr crt, notBefore.cstring, notAfter.cstring + ) + if ret != 0: + raise newException(CertificateCreationError, "Failed to set certificate validity period") + + # Assign the Public Key to the Certificate + mbedtls_x509write_crt_set_subject_key(addr crt, addr certKey) + mbedtls_x509write_crt_set_issuer_key(addr crt, addr certKey) # Self-signed + + # Add the libp2p Extension + let oid = string.fromBytes(LIBP2P_EXT_OID_DER) + ret = mbedtls_x509write_crt_set_extension( + addr crt, + oid, # OID + oid.len.uint, # OID length + 0, # Critical flag + unsafeAddr libp2pExtension[0], # Extension data + libp2pExtension.len.uint, # Extension data length + ) + if ret != 0: + raise newException(CertificateCreationError, "Failed to set libp2p extension in certificate") + + # Set Basic Constraints (optional, e.g., CA:FALSE) + ret = mbedtls_x509write_crt_set_basic_constraints( + addr crt, + 0, # is_ca + -1, # max_pathlen (-1 for no limit) + ) + if ret != 0: + raise newException(CertificateCreationError, "Failed to set basic constraints") + + # Set Key Usage + ret = mbedtls_x509write_crt_set_key_usage( + addr crt, MBEDTLS_X509_KU_DIGITAL_SIGNATURE or MBEDTLS_X509_KU_KEY_ENCIPHERMENT + ) + if ret != 0: + raise newException(CertificateCreationError, "Failed to set key usage") + + # Set the MD algorithm + mbedtls_x509write_crt_set_md_alg(addr crt, SIGNATURE_ALG) + + # Prepare Buffer for Certificate Serialization + const CERT_BUFFER_SIZE = 4096 + var + certBuffer: array[CERT_BUFFER_SIZE, byte] + certLen: cint + + # Write the Certificate in DER Format + certLen = mbedtls_x509write_crt_der( + addr crt, + addr certBuffer[0], + CERT_BUFFER_SIZE.uint, + mbedtls_ctr_drbg_random, + addr ctrDrbg + ) + if certLen < 0: + raise newException(CertificateCreationError, "Failed to write certificate in DER format") + + # Adjust the buffer to contain only the DER data + let certificateDer = toSeq(certBuffer[ + (CERT_BUFFER_SIZE - certLen) ..< CERT_BUFFER_SIZE + ]) + + # Serialize the Private Key in DER Format (PKCS#8) + var + privKeyBuffer: array[2048, byte] + privKeyLen: cint + + privKeyLen = mbedtls_pk_write_key_der( + addr certKey, addr privKeyBuffer[0], privKeyBuffer.len.uint + ) + if privKeyLen < 0: + raise newException(CertificateCreationError, "Failed to write private key in DER format") + + # Adjust the buffer to contain only the DER data + let privateKeyDer = toSeq(privKeyBuffer[ + (privKeyBuffer.len - privKeyLen) ..< privKeyBuffer.len + ]) + + # Return the Serialized Certificate and Private Key + return (certificateDer, privateKeyDer) + +proc libp2pext( + p_ctx: pointer; crt: ptr mbedtls_x509_crt; + oid: ptr mbedtls_x509_buf; critical: cint; + p: ptr byte; endPtr: ptr byte + ): cint {.cdecl.} = + ## Callback function to parse the libp2p extension. + ## + ## This function is used as a callback by mbedtls during certificate parsing + ## to extract the libp2p extension containing the SignedKey. + ## + ## Parameters: + ## - `p_ctx`: Pointer to the P2pExtension object to store the parsed data. + ## - `crt`: Pointer to the certificate being parsed. + ## - `oid`: Pointer to the OID of the extension. + ## - `critical`: Critical flag of the extension. + ## - `p`: Pointer to the start of the extension data. + ## - `endPtr`: Pointer to the end of the extension data. + ## + ## Returns: + ## - 0 on success, or a negative error code on failure. + + # Check if the OID matches the libp2p extension + if oid.len != LIBP2P_EXT_OID_DER.len: + return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback + for i in 0 ..< LIBP2P_EXT_OID_DER.len: + if ptrInc(oid.p, i.uint)[] != LIBP2P_EXT_OID_DER[i]: + return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback + + var parsePtr = p + + # Parse SEQUENCE tag and length + var len: uint + if mbedtls_asn1_get_tag( + addr parsePtr, endPtr, addr len, + MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE + ) != 0: + debug "Failed to parse SEQUENCE in libp2p extension" + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + + # Parse publicKey OCTET STRING + var pubKeyLen: uint + if mbedtls_asn1_get_tag( + addr parsePtr, endPtr, addr pubKeyLen, MBEDTLS_ASN1_OCTET_STRING + ) != 0: + debug "Failed to parse publicKey OCTET STRING in libp2p extension" + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + + # Extract publicKey + var publicKey = newSeq[byte](int(pubKeyLen)) + copyMem(addr publicKey[0], parsePtr, int(pubKeyLen)) + parsePtr = ptrInc(parsePtr, pubKeyLen) + + + # Parse signature OCTET STRING + var signatureLen: uint + if mbedtls_asn1_get_tag( + addr parsePtr, endPtr, addr signatureLen, MBEDTLS_ASN1_OCTET_STRING + ) != 0: + debug "Failed to parse signature OCTET STRING in libp2p extension" + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + + # Extract signature + var signature = newSeq[byte](int(signatureLen)) + copyMem(addr signature[0], parsePtr, int(signatureLen)) + + # Store the publicKey and signature in the P2pExtension + let extension = cast[ptr P2pExtension](p_ctx) + extension[].publicKey = publicKey + extension[].signature = signature + + return 0 # Success + +proc parse*(certificateDer: seq[byte]): P2pCertificate {.raises: [CertificateParsingError].} = + ## Parses a DER-encoded certificate and extracts the P2pCertificate. + ## + ## Parameters: + ## - `certificateDer`: The DER-encoded certificate bytes. + ## + ## Returns: + ## A `P2pCertificate` object containing the certificate and its libp2p extension. + ## + ## Raises: + ## - `CertificateParsingError` if certificate parsing fails. + var crt: mbedtls_x509_crt + mbedtls_x509_crt_init(addr crt) + defer: + mbedtls_x509_crt_free(addr crt) + + var extension = P2pExtension() + let ret = mbedtls_x509_crt_parse_der_with_ext_cb( + addr crt, + unsafeAddr certificateDer[0], + certificateDer.len.uint, + 0, + libp2pext, + addr extension + ) + if ret != 0: + raise newException(CertificateParsingError, "Failed to parse certificate, error code: " & $ret) + + return P2pCertificate(certificate: crt, extension: extension) diff --git a/tests/transports/tls/testcertificate.nim b/tests/transports/tls/testcertificate.nim new file mode 100644 index 0000000000..b5ddcdfb67 --- /dev/null +++ b/tests/transports/tls/testcertificate.nim @@ -0,0 +1,21 @@ +import unittest2 + +import ../../../libp2p/transports/tls/certificate +import ../../../libp2p/crypto/crypto +import ../../../libp2p/peerid + +suite "Certificate Tests": + + test "sanity check": + var rng = newRng() + + # Generate an Ed25519 keypair + let keypair = KeyPair.random(Secp256k1, rng[]).tryGet() + let peerId = PeerId.init(keypair.pubkey).tryGet() + + let certBytes = generate(keypair)[0] + let cert = parse(certBytes) + let ext = cert.extension + + let parsedPeerId = PeerId.init(PublicKey.init(ext.publicKey).tryGet).tryGet() + check parsedPeerId == peerId \ No newline at end of file From 3441b1e527c1a77d77388641de92d5492bc9e839 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 3 Oct 2024 17:09:34 +0200 Subject: [PATCH 2/8] chore: formatting --- libp2p/transports/tls/certificate.nim | 176 ++++++++++++----------- tests/transports/tls/testcertificate.nim | 3 +- 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index 339f73ddc6..440e1b8f9e 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -36,9 +36,9 @@ const P2P_SIGNING_PREFIX = "libp2p-tls-handshake:" SIGNATURE_ALG = MBEDTLS_MD_SHA256 EC_GROUP_ID = MBEDTLS_ECP_DP_SECP256R1 - LIBP2P_EXT_OID_DER: array[10, byte] = [ - 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0xA2, 0x5A, 0x01, 0x01 - ] # "1.3.6.1.4.1.53594.1.1" + LIBP2P_EXT_OID_DER: array[10, byte] = + [0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0xA2, 0x5A, 0x01, 0x01] + # "1.3.6.1.4.1.53594.1.1" # Exception types for TLS certificate errors type @@ -64,7 +64,9 @@ proc ptrInc*(p: ptr byte, n: uint): ptr byte = ## Utility function to increment a pointer by n bytes. cast[ptr byte](cast[uint](p) + n) -proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.raises: [ASN1EncodingError].} = +proc generateSignedKey( + signature: seq[byte], pubKey: seq[byte] +): seq[byte] {.raises: [ASN1EncodingError].} = ## Generates the ASN.1-encoded SignedKey structure. ## ## The SignedKey structure contains the public key and its signature, @@ -79,8 +81,7 @@ proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.rai ## ## Raises: ## - `ASN1EncodingError` if ASN.1 encoding fails. - const - extValueSize = 256 # Buffer size for ASN.1 encoding + const extValueSize = 256 # Buffer size for ASN.1 encoding var extValue: array[extValueSize, byte] extPtr: ptr byte = addr extValue[extValueSize - 1] # Start at the end of the buffer @@ -89,10 +90,7 @@ proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.rai # Write signature OCTET STRING let retSig = mbedtls_asn1_write_octet_string( - addr extPtr, - startPtr, - unsafeAddr signature[0], - signature.len.uint + addr extPtr, startPtr, unsafeAddr signature[0], signature.len.uint ) if retSig < 0: raise newException(ASN1EncodingError, "Failed to write signature OCTET STRING") @@ -100,10 +98,7 @@ proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.rai # Write publicKey OCTET STRING let retPub = mbedtls_asn1_write_octet_string( - addr extPtr, - startPtr, - unsafeAddr pubKey[0], - pubKey.len.uint + addr extPtr, startPtr, unsafeAddr pubKey[0], pubKey.len.uint ) if retPub < 0: raise newException(ASN1EncodingError, "Failed to write publicKey OCTET STRING") @@ -112,20 +107,14 @@ proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.rai # Total length of the SEQUENCE contents let contentLen = retSig + retPub # Write SEQUENCE length - let retLen = mbedtls_asn1_write_len( - addr extPtr, - startPtr, - contentLen.uint - ) + let retLen = mbedtls_asn1_write_len(addr extPtr, startPtr, contentLen.uint) if retLen < 0: raise newException(ASN1EncodingError, "Failed to write SEQUENCE length") len += retLen # Write SEQUENCE tag let retTag = mbedtls_asn1_write_tag( - addr extPtr, - startPtr, - MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE + addr extPtr, startPtr, MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE ) if retTag < 0: raise newException(ASN1EncodingError, "Failed to write SEQUENCE tag") @@ -142,7 +131,12 @@ proc generateSignedKey(signature: seq[byte], pubKey: seq[byte]): seq[byte] {.rai proc makeLibp2pExtension( identityKeypair: KeyPair, certificateKeypair: mbedtls_pk_context -): seq[byte] {.raises: [CertificateCreationError, IdentityPubKeySerializationError, IdentitySigningError, ASN1EncodingError, TLSCertificateError].} = +): seq[byte] {. + raises: [ + CertificateCreationError, IdentityPubKeySerializationError, IdentitySigningError, + ASN1EncodingError, TLSCertificateError, + ] +.} = ## Creates the libp2p extension containing the SignedKey. ## ## The libp2p extension is an ASN.1-encoded structure that includes @@ -167,16 +161,15 @@ proc makeLibp2pExtension( certPubKeyDerLen: cint certPubKeyDerLen = mbedtls_pk_write_pubkey_der( - unsafeAddr certificateKeypair, - addr certPubKeyDer[0], - certPubKeyDer.len.uint + unsafeAddr certificateKeypair, addr certPubKeyDer[0], certPubKeyDer.len.uint ) if certPubKeyDerLen < 0: - raise newException(CertificateCreationError, "Failed to write certificate public key in DER format") + raise newException( + CertificateCreationError, "Failed to write certificate public key in DER format" + ) # Adjust pointer to the start of the data - let certPubKeyDerPtr = - addr certPubKeyDer[certPubKeyDer.len - certPubKeyDerLen] + let certPubKeyDerPtr = addr certPubKeyDer[certPubKeyDer.len - certPubKeyDerLen] # Create the Message to Sign var msg = newSeq[byte](P2P_SIGNING_PREFIX.len + certPubKeyDerLen.int.int) @@ -186,16 +179,12 @@ proc makeLibp2pExtension( msg[i] = byte(P2P_SIGNING_PREFIX[i]) # Copy the public key DER into msg - copyMem( - addr msg[P2P_SIGNING_PREFIX.len], - certPubKeyDerPtr, - certPubKeyDerLen.int - ) + copyMem(addr msg[P2P_SIGNING_PREFIX.len], certPubKeyDerPtr, certPubKeyDerLen.int) # Compute SHA-256 hash of the message var hash: array[32, byte] let hashRet = mbedtls_sha256( - msg[0].addr, msg.len.uint, addr hash[0], 0 # 0 for SHA-256 + msg[0].addr, msg.len.uint, addr hash[0], 0 # 0 for SHA-256 ) if hashRet != 0: # Since hashing failure is critical and unlikely, we can raise a general exception @@ -204,20 +193,30 @@ proc makeLibp2pExtension( # Sign the hash with the Identity Key let signatureResult = identityKeypair.seckey.sign(hash) if signatureResult.isErr: - raise newException(IdentitySigningError, "Failed to sign the hash with the identity key") + raise newException( + IdentitySigningError, "Failed to sign the hash with the identity key" + ) let signature = signatureResult.get().data # Get the public key bytes let pubKeyBytesResult = identityKeypair.pubkey.getBytes() if pubKeyBytesResult.isErr: - raise newException(IdentityPubKeySerializationError, "Failed to get identity public key bytes") + raise newException( + IdentityPubKeySerializationError, "Failed to get identity public key bytes" + ) let pubKeyBytes = pubKeyBytesResult.get() # Generate the SignedKey ASN.1 structure return generateSignedKey(signature, pubKeyBytes) -proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) - {.raises: [KeyGenerationError, CertificateCreationError, ASN1EncodingError, IdentityPubKeySerializationError, IdentitySigningError, TLSCertificateError].} = +proc generate*( + identityKeyPair: KeyPair +): (seq[byte], seq[byte]) {. + raises: [ + KeyGenerationError, CertificateCreationError, ASN1EncodingError, + IdentityPubKeySerializationError, IdentitySigningError, TLSCertificateError, + ] +.} = ## Generates a self-signed X.509 certificate with the libp2p extension. ## ## Parameters: @@ -258,29 +257,26 @@ proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) mbedtls_entropy_func, addr entropy, cast[ptr byte](personalization.cstring), - personalization.len.uint + personalization.len.uint, ) if ret != 0: raise newException(KeyGenerationError, "Failed to seed CTR_DRBG") # Initialize certificate key - ret = mbedtls_pk_setup( - addr certKey, - mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY) - ) + ret = mbedtls_pk_setup(addr certKey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) if ret != 0: raise newException(KeyGenerationError, "Failed to set up certificate key context") # Generate key pair for the certificate - let G = try: - mb_pk_ec(certKey) - except MbedTLSError as e: - raise newException(KeyGenerationError, e.msg) - ret = mbedtls_ecp_gen_key( - EC_GROUP_ID, G, mbedtls_ctr_drbg_random, addr ctrDrbg - ) + let G = + try: + mb_pk_ec(certKey) + except MbedTLSError as e: + raise newException(KeyGenerationError, e.msg) + ret = mbedtls_ecp_gen_key(EC_GROUP_ID, G, mbedtls_ctr_drbg_random, addr ctrDrbg) if ret != 0: - raise newException(KeyGenerationError, "Failed to generate EC key pair for certificate") + raise + newException(KeyGenerationError, "Failed to generate EC key pair for certificate") ## Initialize libp2p extension let libp2pExtension = makeLibp2pExtension(identityKeyPair, certKey) @@ -297,15 +293,16 @@ proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) # Set Validity Period let notBefore = "19750101000000" let notAfter = "40960101000000" - ret = mbedtls_x509write_crt_set_validity( - addr crt, notBefore.cstring, notAfter.cstring - ) + ret = + mbedtls_x509write_crt_set_validity(addr crt, notBefore.cstring, notAfter.cstring) if ret != 0: - raise newException(CertificateCreationError, "Failed to set certificate validity period") + raise newException( + CertificateCreationError, "Failed to set certificate validity period" + ) # Assign the Public Key to the Certificate mbedtls_x509write_crt_set_subject_key(addr crt, addr certKey) - mbedtls_x509write_crt_set_issuer_key(addr crt, addr certKey) # Self-signed + mbedtls_x509write_crt_set_issuer_key(addr crt, addr certKey) # Self-signed # Add the libp2p Extension let oid = string.fromBytes(LIBP2P_EXT_OID_DER) @@ -318,7 +315,9 @@ proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) libp2pExtension.len.uint, # Extension data length ) if ret != 0: - raise newException(CertificateCreationError, "Failed to set libp2p extension in certificate") + raise newException( + CertificateCreationError, "Failed to set libp2p extension in certificate" + ) # Set Basic Constraints (optional, e.g., CA:FALSE) ret = mbedtls_x509write_crt_set_basic_constraints( @@ -351,15 +350,16 @@ proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) addr certBuffer[0], CERT_BUFFER_SIZE.uint, mbedtls_ctr_drbg_random, - addr ctrDrbg + addr ctrDrbg, ) if certLen < 0: - raise newException(CertificateCreationError, "Failed to write certificate in DER format") + raise newException( + CertificateCreationError, "Failed to write certificate in DER format" + ) # Adjust the buffer to contain only the DER data - let certificateDer = toSeq(certBuffer[ - (CERT_BUFFER_SIZE - certLen) ..< CERT_BUFFER_SIZE - ]) + let certificateDer = + toSeq(certBuffer[(CERT_BUFFER_SIZE - certLen) ..< CERT_BUFFER_SIZE]) # Serialize the Private Key in DER Format (PKCS#8) var @@ -370,21 +370,25 @@ proc generate*(identityKeyPair: KeyPair): (seq[byte], seq[byte]) addr certKey, addr privKeyBuffer[0], privKeyBuffer.len.uint ) if privKeyLen < 0: - raise newException(CertificateCreationError, "Failed to write private key in DER format") + raise newException( + CertificateCreationError, "Failed to write private key in DER format" + ) # Adjust the buffer to contain only the DER data - let privateKeyDer = toSeq(privKeyBuffer[ - (privKeyBuffer.len - privKeyLen) ..< privKeyBuffer.len - ]) + let privateKeyDer = + toSeq(privKeyBuffer[(privKeyBuffer.len - privKeyLen) ..< privKeyBuffer.len]) # Return the Serialized Certificate and Private Key return (certificateDer, privateKeyDer) proc libp2pext( - p_ctx: pointer; crt: ptr mbedtls_x509_crt; - oid: ptr mbedtls_x509_buf; critical: cint; - p: ptr byte; endPtr: ptr byte - ): cint {.cdecl.} = + p_ctx: pointer, + crt: ptr mbedtls_x509_crt, + oid: ptr mbedtls_x509_buf, + critical: cint, + p: ptr byte, + endPtr: ptr byte, +): cint {.cdecl.} = ## Callback function to parse the libp2p extension. ## ## This function is used as a callback by mbedtls during certificate parsing @@ -403,27 +407,26 @@ proc libp2pext( # Check if the OID matches the libp2p extension if oid.len != LIBP2P_EXT_OID_DER.len: - return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback + return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback for i in 0 ..< LIBP2P_EXT_OID_DER.len: if ptrInc(oid.p, i.uint)[] != LIBP2P_EXT_OID_DER[i]: - return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback + return MBEDTLS_ERR_OID_NOT_FOUND # Extension not handled by this callback var parsePtr = p # Parse SEQUENCE tag and length var len: uint if mbedtls_asn1_get_tag( - addr parsePtr, endPtr, addr len, - MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE - ) != 0: + addr parsePtr, endPtr, addr len, MBEDTLS_ASN1_CONSTRUCTED or MBEDTLS_ASN1_SEQUENCE + ) != 0: debug "Failed to parse SEQUENCE in libp2p extension" return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG # Parse publicKey OCTET STRING var pubKeyLen: uint if mbedtls_asn1_get_tag( - addr parsePtr, endPtr, addr pubKeyLen, MBEDTLS_ASN1_OCTET_STRING - ) != 0: + addr parsePtr, endPtr, addr pubKeyLen, MBEDTLS_ASN1_OCTET_STRING + ) != 0: debug "Failed to parse publicKey OCTET STRING in libp2p extension" return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG @@ -432,12 +435,11 @@ proc libp2pext( copyMem(addr publicKey[0], parsePtr, int(pubKeyLen)) parsePtr = ptrInc(parsePtr, pubKeyLen) - # Parse signature OCTET STRING var signatureLen: uint if mbedtls_asn1_get_tag( - addr parsePtr, endPtr, addr signatureLen, MBEDTLS_ASN1_OCTET_STRING - ) != 0: + addr parsePtr, endPtr, addr signatureLen, MBEDTLS_ASN1_OCTET_STRING + ) != 0: debug "Failed to parse signature OCTET STRING in libp2p extension" return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG @@ -450,9 +452,11 @@ proc libp2pext( extension[].publicKey = publicKey extension[].signature = signature - return 0 # Success + return 0 # Success -proc parse*(certificateDer: seq[byte]): P2pCertificate {.raises: [CertificateParsingError].} = +proc parse*( + certificateDer: seq[byte] +): P2pCertificate {.raises: [CertificateParsingError].} = ## Parses a DER-encoded certificate and extracts the P2pCertificate. ## ## Parameters: @@ -475,9 +479,11 @@ proc parse*(certificateDer: seq[byte]): P2pCertificate {.raises: [CertificatePar certificateDer.len.uint, 0, libp2pext, - addr extension + addr extension, ) if ret != 0: - raise newException(CertificateParsingError, "Failed to parse certificate, error code: " & $ret) + raise newException( + CertificateParsingError, "Failed to parse certificate, error code: " & $ret + ) return P2pCertificate(certificate: crt, extension: extension) diff --git a/tests/transports/tls/testcertificate.nim b/tests/transports/tls/testcertificate.nim index b5ddcdfb67..47a90529ad 100644 --- a/tests/transports/tls/testcertificate.nim +++ b/tests/transports/tls/testcertificate.nim @@ -5,7 +5,6 @@ import ../../../libp2p/crypto/crypto import ../../../libp2p/peerid suite "Certificate Tests": - test "sanity check": var rng = newRng() @@ -18,4 +17,4 @@ suite "Certificate Tests": let ext = cert.extension let parsedPeerId = PeerId.init(PublicKey.init(ext.publicKey).tryGet).tryGet() - check parsedPeerId == peerId \ No newline at end of file + check parsedPeerId == peerId From fca5555bf3dc0d197c29dfea117ca8d021689322 Mon Sep 17 00:00:00 2001 From: Diego Date: Mon, 14 Oct 2024 17:35:42 +0200 Subject: [PATCH 3/8] chore: improve comment about buffer --- libp2p/transports/tls/certificate.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index 440e1b8f9e..dafd94069b 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -84,7 +84,8 @@ proc generateSignedKey( const extValueSize = 256 # Buffer size for ASN.1 encoding var extValue: array[extValueSize, byte] - extPtr: ptr byte = addr extValue[extValueSize - 1] # Start at the end of the buffer + extPtr: ptr byte = addr extValue[extValueSize - 1] + # Start at the end of the buffer as mbedtls_asn1_write_octet_string works backwards in data buffer. startPtr: ptr byte = addr extValue[0] len = 0 From 0e7b18f505e205fcc3fa3b2a27f4c83a38ce4129 Mon Sep 17 00:00:00 2001 From: Diego Date: Mon, 14 Oct 2024 19:00:22 +0200 Subject: [PATCH 4/8] chore: use LPError --- libp2p/transports/tls/certificate.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index dafd94069b..2473a8a78c 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -27,6 +27,7 @@ import mbedtls/debug import mbedtls/error import nimcrypto/utils import ../../crypto/crypto +import ../../errors logScope: topics = "libp2p tls certificate" @@ -42,7 +43,7 @@ const # Exception types for TLS certificate errors type - TLSCertificateError* = object of Exception + TLSCertificateError* = object of LPError ASN1EncodingError* = object of TLSCertificateError KeyGenerationError* = object of TLSCertificateError CertificateCreationError* = object of TLSCertificateError From 7587e0f33f42e7d3c6b809cc4d07b1ee83bc5b2b Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 17 Oct 2024 19:07:04 +0200 Subject: [PATCH 5/8] chore: initialize entropy and DRBG context only once --- libp2p/transports/tls/certificate.nim | 46 +++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index 2473a8a78c..e7bf9ab366 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -7,14 +7,14 @@ # This file may not be copied, modified, or distributed except according to # those terms. -import std/[sequtils, strutils] +import std/[sequtils, strutils, exitprocs] import stew/byteutils import chronicles import mbedtls/pk -import mbedtls/ctr_drbg -import mbedtls/entropy +import mbedtls/ctr_drbg as ctr_drbg_module +import mbedtls/entropy as entropy_module import mbedtls/ecp import mbedtls/sha256 import mbedtls/md @@ -65,6 +65,41 @@ proc ptrInc*(p: ptr byte, n: uint): ptr byte = ## Utility function to increment a pointer by n bytes. cast[ptr byte](cast[uint](p) + n) +# Initialize entropy and DRBG contexts at the module level +var + entropy: mbedtls_entropy_context + ctrDrbg: mbedtls_ctr_drbg_context + drbgInitialized = false + +proc initializeDRBG() = + ## Function to initialize entropy and DRBG context if not already initialized. + if not drbgInitialized: + mbedtls_entropy_init(addr entropy) + mbedtls_ctr_drbg_init(addr ctrDrbg) + + # Seed the random number generator + let personalization = "libp2p_tls" + let ret = mbedtls_ctr_drbg_seed( + addr ctrDrbg, + mbedtls_entropy_func, + addr entropy, + cast[ptr byte](personalization.cstring), + personalization.len.uint + ) + if ret != 0: + raise newException(KeyGenerationError, "Failed to seed CTR_DRBG") + drbgInitialized = true + +proc cleanupDRBG() = + ## Function to free entropy and DRBG context. + if drbgInitialized: + mbedtls_ctr_drbg_free(addr ctrDrbg) + mbedtls_entropy_free(addr entropy) + drbgInitialized = false + +# Register cleanup function to free entropy and DRBG context +addExitProc(cleanupDRBG) + proc generateSignedKey( signature: seq[byte], pubKey: seq[byte] ): seq[byte] {.raises: [ASN1EncodingError].} = @@ -233,10 +268,9 @@ proc generate*( ## - `KeyGenerationError` if key generation fails. ## - `CertificateCreationError` if certificate creation fails. ## - `ASN1EncodingError` if encoding fails. - # Initialize entropy and DRBG contexts + # Ensure DRBG contexts are initialized + initializeDRBG() var - entropy = mbedtls_entropy_context() - ctrDrbg = mbedtls_ctr_drbg_context() crt: mbedtls_x509write_cert certKey: mbedtls_pk_context ret: cint From b30c7af472c5b5b81088d51a245c9c56485f7fec Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Thu, 6 Feb 2025 18:51:23 -0400 Subject: [PATCH 6/8] fix: add serial number to certificate --- libp2p/transports/tls/certificate.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index e7bf9ab366..c79e719c92 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -374,6 +374,18 @@ proc generate*( # Set the MD algorithm mbedtls_x509write_crt_set_md_alg(addr crt, SIGNATURE_ALG) + # Generate a random serial number + const SERIAL_LEN = 20 + var serialBuffer: array[SERIAL_LEN, byte] + ret = mbedtls_ctr_drbg_random(addr ctrDrbg, addr serialBuffer[0], SERIAL_LEN); + if ret != 0: + raise newException(CertificateCreationError, "Failed to generate serial number") + + # Set the serial number + ret = mbedtls_x509write_crt_set_serial_raw(addr crt, addr serialBuffer[0], SERIAL_LEN); + if ret != 0: + raise newException(CertificateCreationError, "Failed to set serial number") + # Prepare Buffer for Certificate Serialization const CERT_BUFFER_SIZE = 4096 var From 80f838576858f435efbdef4792e2e38e1dec3953 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Thu, 20 Feb 2025 16:15:45 -0400 Subject: [PATCH 7/8] feat: encode certificates in PEM or DER --- libp2p/transports/tls/certificate.nim | 109 ++++++++++++++--------- tests/transports/tls/testcertificate.nim | 2 +- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/libp2p/transports/tls/certificate.nim b/libp2p/transports/tls/certificate.nim index c79e719c92..88c34f935f 100644 --- a/libp2p/transports/tls/certificate.nim +++ b/libp2p/transports/tls/certificate.nim @@ -61,6 +61,10 @@ type certificate*: mbedtls_x509_crt extension*: P2pExtension +type EncodingFormat* = enum + DER + PEM + proc ptrInc*(p: ptr byte, n: uint): ptr byte = ## Utility function to increment a pointer by n bytes. cast[ptr byte](cast[uint](p) + n) @@ -84,7 +88,7 @@ proc initializeDRBG() = mbedtls_entropy_func, addr entropy, cast[ptr byte](personalization.cstring), - personalization.len.uint + personalization.len.uint, ) if ret != 0: raise newException(KeyGenerationError, "Failed to seed CTR_DRBG") @@ -247,7 +251,7 @@ proc makeLibp2pExtension( return generateSignedKey(signature, pubKeyBytes) proc generate*( - identityKeyPair: KeyPair + identityKeyPair: KeyPair, encodingFormat: EncodingFormat = EncodingFormat.DER ): (seq[byte], seq[byte]) {. raises: [ KeyGenerationError, CertificateCreationError, ASN1EncodingError, @@ -261,8 +265,8 @@ proc generate*( ## ## Returns: ## A tuple containing: - ## - The DER-encoded certificate. - ## - The DER-encoded private key. + ## - The certificate. + ## - The private key. ## ## Raises: ## - `KeyGenerationError` if key generation fails. @@ -377,57 +381,78 @@ proc generate*( # Generate a random serial number const SERIAL_LEN = 20 var serialBuffer: array[SERIAL_LEN, byte] - ret = mbedtls_ctr_drbg_random(addr ctrDrbg, addr serialBuffer[0], SERIAL_LEN); + ret = mbedtls_ctr_drbg_random(addr ctrDrbg, addr serialBuffer[0], SERIAL_LEN) if ret != 0: raise newException(CertificateCreationError, "Failed to generate serial number") # Set the serial number - ret = mbedtls_x509write_crt_set_serial_raw(addr crt, addr serialBuffer[0], SERIAL_LEN); + ret = mbedtls_x509write_crt_set_serial_raw(addr crt, addr serialBuffer[0], SERIAL_LEN) if ret != 0: raise newException(CertificateCreationError, "Failed to set serial number") # Prepare Buffer for Certificate Serialization const CERT_BUFFER_SIZE = 4096 - var - certBuffer: array[CERT_BUFFER_SIZE, byte] - certLen: cint - - # Write the Certificate in DER Format - certLen = mbedtls_x509write_crt_der( - addr crt, - addr certBuffer[0], - CERT_BUFFER_SIZE.uint, - mbedtls_ctr_drbg_random, - addr ctrDrbg, - ) - if certLen < 0: - raise newException( - CertificateCreationError, "Failed to write certificate in DER format" + var certBuffer: array[CERT_BUFFER_SIZE, byte] + var outputCertificate: seq[byte] + + if encodingFormat == EncodingFormat.DER: + let certLen: cint = mbedtls_x509write_crt_der( + addr crt, + addr certBuffer[0], + CERT_BUFFER_SIZE.uint, + mbedtls_ctr_drbg_random, + addr ctrDrbg, ) - - # Adjust the buffer to contain only the DER data - let certificateDer = - toSeq(certBuffer[(CERT_BUFFER_SIZE - certLen) ..< CERT_BUFFER_SIZE]) - - # Serialize the Private Key in DER Format (PKCS#8) - var - privKeyBuffer: array[2048, byte] - privKeyLen: cint - - privKeyLen = mbedtls_pk_write_key_der( - addr certKey, addr privKeyBuffer[0], privKeyBuffer.len.uint - ) - if privKeyLen < 0: - raise newException( - CertificateCreationError, "Failed to write private key in DER format" + if certLen < 0: + raise newException( + CertificateCreationError, "Failed to write certificate in DER format" + ) + # Adjust the buffer to contain only the data + outputCertificate = + toSeq(certBuffer[(CERT_BUFFER_SIZE - certLen) ..< CERT_BUFFER_SIZE]) + else: + let ret = mbedtls_x509write_crt_pem( + addr crt, + addr certBuffer[0], + CERT_BUFFER_SIZE.uint, + mbedtls_ctr_drbg_random, + addr ctrDrbg, ) - - # Adjust the buffer to contain only the DER data - let privateKeyDer = - toSeq(privKeyBuffer[(privKeyBuffer.len - privKeyLen) ..< privKeyBuffer.len]) + if ret != 0: + raise newException( + CertificateCreationError, "Failed to write certificate in PEM format" + ) + let n = certBuffer.find(0'u8) # Find the index of the first null byte + outputCertificate = certBuffer[0 .. n - 1].toSeq() + + # Serialize the Private Key + var privKeyBuffer: array[2048, byte] + var outputPrivateKey: seq[byte] + + if encodingFormat == EncodingFormat.DER: + let privKeyLen = mbedtls_pk_write_key_der( + addr certKey, addr privKeyBuffer[0], privKeyBuffer.len.uint + ) + if privKeyLen < 0: + raise newException( + CertificateCreationError, "Failed to write private key in DER format" + ) + # Adjust the buffer to contain only the data + outputPrivateKey = + toSeq(privKeyBuffer[(privKeyBuffer.len - privKeyLen) ..< privKeyBuffer.len]) + else: + let ret = mbedtls_pk_write_key_pem( + addr certKey, addr privKeyBuffer[0], privKeyBuffer.len.uint + ) + if ret != 0: + raise newException( + CertificateCreationError, "Failed to write private key in PEM format" + ) + let n = privKeyBuffer.find(0'u8) # Find the index of the first null byte + outputPrivateKey = privKeyBuffer[0 .. n - 1].toSeq() # Return the Serialized Certificate and Private Key - return (certificateDer, privateKeyDer) + return (outputCertificate, outputPrivateKey) proc libp2pext( p_ctx: pointer, diff --git a/tests/transports/tls/testcertificate.nim b/tests/transports/tls/testcertificate.nim index 47a90529ad..6c13a5de21 100644 --- a/tests/transports/tls/testcertificate.nim +++ b/tests/transports/tls/testcertificate.nim @@ -12,7 +12,7 @@ suite "Certificate Tests": let keypair = KeyPair.random(Secp256k1, rng[]).tryGet() let peerId = PeerId.init(keypair.pubkey).tryGet() - let certBytes = generate(keypair)[0] + let (certBytes, _) = generate(keypair, EncodingFormat.DER) let cert = parse(certBytes) let ext = cert.extension From 74a1ea8ae5acd0736cd3ac19141592229fb5f6a1 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Fri, 28 Feb 2025 09:46:44 -0400 Subject: [PATCH 8/8] chore: fix ci --- .github/workflows/ci.yml | 8 ++++++++ tests/hole-punching-interop/Dockerfile | 4 ++++ tests/transport-interop/Dockerfile | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1a6c4fe42..1195c804be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,9 +96,15 @@ jobs: # The change happened on Nimble v0.14.0. Also forcing the deps to be reinstalled on each os and cpu. key: nimbledeps-${{ matrix.nim.ref }}-${{ matrix.builder }}-${{ matrix.platform.cpu }}-${{ hashFiles('.pinned') }} # hashFiles returns a different value on windows + - name: Setup python + run: | + mkdir .venv + python -m venv .venv + - name: Install deps if: ${{ steps.deps-cache.outputs.cache-hit != 'true' }} run: | + source .venv/bin/activate nimble install_pinned - name: Use gcc 14 @@ -112,6 +118,8 @@ jobs: - name: Run tests run: | + source .venv/bin/activate + nim --version nimble --version gcc --version diff --git a/tests/hole-punching-interop/Dockerfile b/tests/hole-punching-interop/Dockerfile index f7dd1b0064..3f2030c93a 100644 --- a/tests/hole-punching-interop/Dockerfile +++ b/tests/hole-punching-interop/Dockerfile @@ -5,6 +5,10 @@ WORKDIR /workspace COPY .pinned libp2p.nimble nim-libp2p/ +RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y python python3 python3-pip python3-venv curl + +RUN mkdir .venv && python3 -m venv .venv && . .venv/bin/activate + RUN cd nim-libp2p && nimble install_pinned && nimble install redis -y COPY . nim-libp2p/ diff --git a/tests/transport-interop/Dockerfile b/tests/transport-interop/Dockerfile index e1aa31d390..7ab3a82abb 100644 --- a/tests/transport-interop/Dockerfile +++ b/tests/transport-interop/Dockerfile @@ -5,6 +5,10 @@ WORKDIR /app COPY .pinned libp2p.nimble nim-libp2p/ +RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y python python3 python3-pip python3-venv curl + +RUN mkdir .venv && python3 -m venv .venv && . .venv/bin/activate + RUN cd nim-libp2p && nimble install_pinned && nimble install "redis@#b341fe240dbf11c544011dd0e033d3c3acca56af" -y COPY . nim-libp2p/