Skip to content

Commit

Permalink
Add a Darwin API to parse the CSR out of a nocsr-elements. (#26035)
Browse files Browse the repository at this point in the history
* Add a Darwin API to parse the CSR out of a nocsr-elements.

Addresses the most immediate problem that led to
#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>
  • Loading branch information
2 people authored and pull[bot] committed Dec 14, 2023
1 parent 89d9296 commit 1410340
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 29 deletions.
39 changes: 36 additions & 3 deletions src/darwin/Framework/CHIP/MTRCSRInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#import <Foundation/Foundation.h>

#import <Matter/MTRCommandPayloadsObjc.h>
#import <Matter/MTRDefines.h>

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -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;

Expand All @@ -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))
Expand Down
79 changes: 75 additions & 4 deletions src/darwin/Framework/CHIP/MTRCSRInfo.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,41 @@
*/

#import "MTRCSRInfo.h"
#import "MTRFramework.h"
#import "MTRLogging_Internal.h"
#import "NSDataSpanConversion.h"

#include <credentials/DeviceAttestationConstructor.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/Span.h>

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;
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/darwin/Framework/CHIP/MTROperationalCertificateIssuer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 3 additions & 22 deletions src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}

Expand Down

0 comments on commit 1410340

Please sign in to comment.