From 2206402195659b61f6429f042a37872aaeaf2377 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 11 Apr 2023 02:34:10 -0400 Subject: [PATCH] Add a Darwin API to parse the CSR out of a nocsr-elements. (#26035) * Add a Darwin API to parse the CSR out of a nocsr-elements. Addresses the most immediate problem that led to https://github.com/project-chip/connectedhomeip/issues/24978 being filed. * Update MTRCSRInfo.mm * Update MTRCSRInfo.mm * Address review comment. * Address review comment. --------- Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> --- src/darwin/Framework/CHIP/MTRCSRInfo.h | 39 ++++++++- src/darwin/Framework/CHIP/MTRCSRInfo.mm | 79 ++++++++++++++++++- .../CHIP/MTROperationalCertificateIssuer.h | 4 + .../CHIP/MTROperationalCredentialsDelegate.mm | 25 +----- .../MTROperationalCertificateIssuerTests.m | 7 ++ 5 files changed, 125 insertions(+), 29 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRCSRInfo.h b/src/darwin/Framework/CHIP/MTRCSRInfo.h index b20c34d469a944..c4a218557fc207 100644 --- a/src/darwin/Framework/CHIP/MTRCSRInfo.h +++ b/src/darwin/Framework/CHIP/MTRCSRInfo.h @@ -17,6 +17,7 @@ #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -34,8 +35,7 @@ API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) @property (nonatomic, copy, readonly) MTRCSRDERBytes csr; /** - * The nonce provided in the original CSRRequest command that led to this CSR - * being created. + * The nonce associated with this CSR. */ @property (nonatomic, copy, readonly) NSData * csrNonce; @@ -54,11 +54,44 @@ API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) */ @property (nonatomic, copy, readonly) NSData * attestationSignature; +/** + * Initialize an MTROperationalCSRInfo by providing all the fields. It's the + * caller's responsibility to ensure that csr and csrNonce match the csrElementsTLV. + */ - (instancetype)initWithCSR:(MTRCSRDERBytes)csr csrNonce:(NSData *)csrNonce csrElementsTLV:(MTRTLVBytes)csrElementsTLV - attestationSignature:(NSData *)attestationSignature; + attestationSignature:(NSData *)attestationSignature + MTR_NEWLY_DEPRECATED("Please use one of the initializers that validates the input"); + +/** + * Initialize an MTROperationalCSRInfo by providing the csrNonce (for example, + * the nonce the client initially supplied), and the csrElementsTLV and + * attestationSignature that the server returned. This will ensure that + * csrNonce matches the data in csrElementsTLV, returning nil if it does not, + * and extract the csr from csrElementsTLV. + */ +- (nullable instancetype)initWithCSRNonce:(NSData *)csrNonce + csrElementsTLV:(MTRTLVBytes)csrElementsTLV + attestationSignature:(NSData *)attestationSignature MTR_NEWLY_AVAILABLE; +/** + * Initialize an MTROperationalCSRInfo by providing just the csrElementsTLV and + * attestationSignature (which can come from an + * MTROperationalCredentialsClusterCSRResponseParams). This will extract the + * csr and csrNonce from the csrElementsTLV, if possible, and return nil if that + * fails. + */ +- (nullable instancetype)initWithCSRElementsTLV:(MTRTLVBytes)csrElementsTLV + attestationSignature:(NSData *)attestationSignature MTR_NEWLY_AVAILABLE; + +/** + * Initialize an MTROperationalCSRInfo by providing an + * MTROperationalCredentialsClusterCSRResponseParams. This will extract the + * relevant fields from the response data. + */ +- (nullable instancetype)initWithCSRResponseParams:(MTROperationalCredentialsClusterCSRResponseParams *)responseParams + MTR_NEWLY_AVAILABLE; @end MTR_DEPRECATED("Please use MTROperationalCSRInfo", ios(16.1, 16.4), macos(13.0, 13.3), watchos(9.1, 9.4), tvos(16.1, 16.4)) diff --git a/src/darwin/Framework/CHIP/MTRCSRInfo.mm b/src/darwin/Framework/CHIP/MTRCSRInfo.mm index d8a77fba5e941a..1682a8bdf6c362 100644 --- a/src/darwin/Framework/CHIP/MTRCSRInfo.mm +++ b/src/darwin/Framework/CHIP/MTRCSRInfo.mm @@ -16,15 +16,41 @@ */ #import "MTRCSRInfo.h" +#import "MTRFramework.h" +#import "MTRLogging_Internal.h" +#import "NSDataSpanConversion.h" + +#include +#include +#include +#include + +static CHIP_ERROR ExtractCSRAndNonce(MTRTLVBytes csrElementsTLV, chip::ByteSpan & csr, chip::ByteSpan & nonce) +{ + // We don't care about vendor_reserved*. + chip::ByteSpan vendor_reserved1, vendor_reserved2, vendor_reserved3; + CHIP_ERROR err = chip::Credentials::DeconstructNOCSRElements( + AsByteSpan(csrElementsTLV), csr, nonce, vendor_reserved1, vendor_reserved2, vendor_reserved3); + if (err != CHIP_NO_ERROR) { + MTR_LOG_ERROR("Failed to parse csrElementsTLV %@: %" CHIP_ERROR_FORMAT, csrElementsTLV, err.Format()); + } + return err; +} NS_ASSUME_NONNULL_BEGIN @implementation MTROperationalCSRInfo : NSObject -- (instancetype)initWithCSR:(MTRCSRDERBytes)csr - csrNonce:(NSData *)csrNonce - csrElementsTLV:(MTRTLVBytes)csrElementsTLV - attestationSignature:(NSData *)attestationSignature; ++ (void)initialize +{ + // Needed for some of our init* methods. + MTRFrameworkInit(); +} + +- (instancetype)_initWithValidatedCSR:(MTRCSRDERBytes)csr + csrNonce:(NSData *)csrNonce + csrElementsTLV:(MTRTLVBytes)csrElementsTLV + attestationSignature:(NSData *)attestationSignature; { if (self = [super init]) { _csr = csr; @@ -34,6 +60,51 @@ - (instancetype)initWithCSR:(MTRCSRDERBytes)csr } return self; } + +- (instancetype)initWithCSR:(MTRCSRDERBytes)csr + csrNonce:(NSData *)csrNonce + csrElementsTLV:(MTRTLVBytes)csrElementsTLV + attestationSignature:(NSData *)attestationSignature; +{ + return [self _initWithValidatedCSR:csr + csrNonce:csrNonce + csrElementsTLV:csrElementsTLV + attestationSignature:attestationSignature]; +} + +- (nullable instancetype)initWithCSRNonce:(NSData *)csrNonce + csrElementsTLV:(MTRTLVBytes)csrElementsTLV + attestationSignature:(NSData *)attestationSignature +{ + chip::ByteSpan csr, extractedNonce; + VerifyOrReturnValue(ExtractCSRAndNonce(csrElementsTLV, csr, extractedNonce) == CHIP_NO_ERROR, nil); + + if (!extractedNonce.data_equal(AsByteSpan(csrNonce))) { + MTR_LOG_ERROR("Provided CSR nonce does not match provided csrElementsTLV"); + return nil; + } + + return [self _initWithValidatedCSR:AsData(csr) + csrNonce:csrNonce + csrElementsTLV:csrElementsTLV + attestationSignature:attestationSignature]; +} + +- (nullable instancetype)initWithCSRElementsTLV:(MTRTLVBytes)csrElementsTLV attestationSignature:(NSData *)attestationSignature +{ + chip::ByteSpan csr, csrNonce; + VerifyOrReturnValue(ExtractCSRAndNonce(csrElementsTLV, csr, csrNonce) == CHIP_NO_ERROR, nil); + + return [self _initWithValidatedCSR:AsData(csr) + csrNonce:AsData(csrNonce) + csrElementsTLV:csrElementsTLV + attestationSignature:attestationSignature]; +} + +- (nullable instancetype)initWithCSRResponseParams:(MTROperationalCredentialsClusterCSRResponseParams *)responseParams +{ + return [self initWithCSRElementsTLV:responseParams.nocsrElements attestationSignature:responseParams.attestationSignature]; +} @end @implementation CSRInfo : NSObject diff --git a/src/darwin/Framework/CHIP/MTROperationalCertificateIssuer.h b/src/darwin/Framework/CHIP/MTROperationalCertificateIssuer.h index e97d02ed3e1976..6dbdc869389488 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCertificateIssuer.h +++ b/src/darwin/Framework/CHIP/MTROperationalCertificateIssuer.h @@ -74,6 +74,10 @@ API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) * * This will be called on the dispatch queue passed as * operationalCertificateIssuerQueue in the MTRDeviceControllerFactoryParams. + * + * The csrNonce in the provided MTROperationalCSRInfo will be the nonce that was + * sent in the CSRRequest command, which will be guaranteed, at this point, to + * match the nonce in the CSRResponse command. */ - (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo diff --git a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm index 16ae9da59103ab..b14ac062784105 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm @@ -145,28 +145,9 @@ mOnNOCCompletionCallback = onCompletion; - TLVReader reader; - reader.Init(csrElements); - - if (reader.GetType() == kTLVType_NotSpecified) { - ReturnErrorOnFailure(reader.Next()); - } - - VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE); - VerifyOrReturnError(reader.GetTag() == AnonymousTag(), CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); - - TLVType containerType; - ReturnErrorOnFailure(reader.EnterContainer(containerType)); - ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1))); - - chip::ByteSpan csr; - reader.Get(csr); - reader.ExitContainer(containerType); - - auto * csrInfo = [[MTROperationalCSRInfo alloc] initWithCSR:AsData(csr) - csrNonce:AsData(csrNonce) - csrElementsTLV:AsData(csrElements) - attestationSignature:AsData(csrElementsSignature)]; + auto * csrInfo = [[MTROperationalCSRInfo alloc] initWithCSRNonce:AsData(csrNonce) + csrElementsTLV:AsData(csrElements) + attestationSignature:AsData(csrElementsSignature)]; chip::ByteSpan certificationDeclarationSpan; chip::ByteSpan attestationNonceSpan; diff --git a/src/darwin/Framework/CHIPTests/MTROperationalCertificateIssuerTests.m b/src/darwin/Framework/CHIPTests/MTROperationalCertificateIssuerTests.m index bb9c1597900709..8efc8989abb689 100644 --- a/src/darwin/Framework/CHIPTests/MTROperationalCertificateIssuerTests.m +++ b/src/darwin/Framework/CHIPTests/MTROperationalCertificateIssuerTests.m @@ -98,6 +98,13 @@ - (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo XCTAssertNotNil(attestationInfo); XCTAssertEqual(controller, sController); + __auto_type * csrInfoCopy = [[MTROperationalCSRInfo alloc] initWithCSRElementsTLV:csrInfo.csrElementsTLV + attestationSignature:csrInfo.attestationSignature]; + XCTAssertEqualObjects(csrInfoCopy.csr, csrInfo.csr); + XCTAssertEqualObjects(csrInfoCopy.csrNonce, csrInfo.csrNonce); + XCTAssertEqualObjects(csrInfoCopy.csrElementsTLV, csrInfo.csrElementsTLV); + XCTAssertEqualObjects(csrInfoCopy.attestationSignature, csrInfo.attestationSignature); + completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeIntegrityCheckFailed userInfo:nil]); }