From c75b0a7111ab937fd4ccb1a39e10dd75e1f83cf1 Mon Sep 17 00:00:00 2001
From: Boris Zbarsky <bzbarsky@apple.com>
Date: Wed, 16 Nov 2022 11:03:20 -0500
Subject: [PATCH] Add a Darwin utility to convert Matter TLV certificates to
 DER. (#23628)

We have one for going DER -> TLV, but nothing for TLV -> DER.
---
 src/darwin/Framework/CHIP/MTRCertificates.h   | 10 ++++++
 src/darwin/Framework/CHIP/MTRCertificates.mm  | 17 +++++++++
 .../Framework/CHIPTests/MTRCertificateTests.m | 36 +++++++++++++++++++
 3 files changed, 63 insertions(+)

diff --git a/src/darwin/Framework/CHIP/MTRCertificates.h b/src/darwin/Framework/CHIP/MTRCertificates.h
index 57eac0239a069d..e18878f8773b57 100644
--- a/src/darwin/Framework/CHIP/MTRCertificates.h
+++ b/src/darwin/Framework/CHIP/MTRCertificates.h
@@ -141,6 +141,16 @@ NS_ASSUME_NONNULL_BEGIN
  */
 + (MTRCertificateTLVBytes _Nullable)convertX509Certificate:(MTRCertificateDERBytes)x509Certificate;
 
+/**
+ * Convert the given Matter TLV encoded certificate to the X.509v3 DER encoded
+ * format.
+ *
+ * Returns nil if the conversion fails (e.g. if the input data cannot be parsed
+ * as a Matter TLV encoded certificate, or if the certificate cannot be
+ * represented in the X.509v3 DER format).
+ */
++ (MTRCertificateDERBytes _Nullable)convertMatterCertificate:(MTRCertificateTLVBytes)matterCertificate MTR_NEWLY_AVAILABLE;
+
 @end
 
 @interface MTRCertificates (Deprecated)
diff --git a/src/darwin/Framework/CHIP/MTRCertificates.mm b/src/darwin/Framework/CHIP/MTRCertificates.mm
index 1996cbd10f1845..39735bd79d3771 100644
--- a/src/darwin/Framework/CHIP/MTRCertificates.mm
+++ b/src/darwin/Framework/CHIP/MTRCertificates.mm
@@ -214,6 +214,23 @@ + (MTRCertificateTLVBytes _Nullable)convertX509Certificate:(MTRCertificateDERByt
     return AsData(chipCertBytes);
 }
 
++ (MTRCertificateDERBytes _Nullable)convertMatterCertificate:(MTRCertificateTLVBytes)matterCertificate
+{
+    chip::ByteSpan tlvCertBytes = AsByteSpan(matterCertificate);
+
+    uint8_t derCertBuffer[chip::Controller::kMaxCHIPDERCertLength];
+    chip::MutableByteSpan derCertBytes(derCertBuffer);
+
+    CHIP_ERROR errorCode = chip::Credentials::ConvertChipCertToX509Cert(tlvCertBytes, derCertBytes);
+
+    if (errorCode != CHIP_NO_ERROR) {
+        MTR_LOG_ERROR("ConvertChipCertToX509Cert: %{public}s", chip::ErrorStr(errorCode));
+        return nil;
+    }
+
+    return AsData(derCertBytes);
+}
+
 @end
 
 @implementation MTRCertificates (Deprecated)
diff --git a/src/darwin/Framework/CHIPTests/MTRCertificateTests.m b/src/darwin/Framework/CHIPTests/MTRCertificateTests.m
index e42ea141a1c987..abb6007231ea60 100644
--- a/src/darwin/Framework/CHIPTests/MTRCertificateTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRCertificateTests.m
@@ -34,6 +34,15 @@ - (void)testGenerateRootCert
 
     __auto_type * rootCert = [MTRCertificates createRootCertificate:testKeys issuerID:nil fabricID:nil error:nil];
     XCTAssertNotNil(rootCert);
+
+    // Test round-trip through TLV format.
+    __auto_type * tlvCert = [MTRCertificates convertX509Certificate:rootCert];
+    XCTAssertNotNil(tlvCert);
+
+    __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert];
+    XCTAssertNotNil(derCert);
+
+    XCTAssertEqualObjects(rootCert, derCert);
 }
 
 - (void)testGenerateIntermediateCert
@@ -54,6 +63,15 @@ - (void)testGenerateIntermediateCert
                                                                            fabricID:nil
                                                                               error:nil];
     XCTAssertNotNil(intermediateCert);
+
+    // Test round-trip through TLV format.
+    __auto_type * tlvCert = [MTRCertificates convertX509Certificate:intermediateCert];
+    XCTAssertNotNil(tlvCert);
+
+    __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert];
+    XCTAssertNotNil(derCert);
+
+    XCTAssertEqualObjects(intermediateCert, derCert);
 }
 
 - (void)testGenerateOperationalCertNoIntermediate
@@ -81,6 +99,15 @@ - (void)testGenerateOperationalCertNoIntermediate
                                                             caseAuthenticatedTags:cats
                                                                             error:nil];
     XCTAssertNotNil(operationalCert);
+
+    // Test round-trip through TLV format.
+    __auto_type * tlvCert = [MTRCertificates convertX509Certificate:operationalCert];
+    XCTAssertNotNil(tlvCert);
+
+    __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert];
+    XCTAssertNotNil(derCert);
+
+    XCTAssertEqualObjects(operationalCert, derCert);
 }
 
 - (void)testGenerateOperationalCertWithIntermediate
@@ -113,6 +140,15 @@ - (void)testGenerateOperationalCertWithIntermediate
                                                             caseAuthenticatedTags:nil
                                                                             error:nil];
     XCTAssertNotNil(operationalCert);
+
+    // Test round-trip through TLV format.
+    __auto_type * tlvCert = [MTRCertificates convertX509Certificate:operationalCert];
+    XCTAssertNotNil(tlvCert);
+
+    __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert];
+    XCTAssertNotNil(derCert);
+
+    XCTAssertEqualObjects(operationalCert, derCert);
 }
 
 - (void)testGenerateOperationalCertErrorCases