Skip to content

Commit

Permalink
[Darwin] Add MTROTAHeaderParser and use it in darwin-framework-tool (#…
Browse files Browse the repository at this point in the history
…21235)

* [Darwin] Add MTROTAHeaderParser

* [darwin-framework-tool] Use MTROTAHeaderParser
  • Loading branch information
vivien-apple authored and pull[bot] committed Aug 25, 2023
1 parent 7a07204 commit 2580913
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,27 @@
#include <fstream>
#include <json/json.h>

// TODO: Objective-C Matter.framework needs to expose this.
#include <lib/core/OTAImageHeader.h>

constexpr size_t kOtaHeaderMaxSize = 1024;

bool ParseOTAHeader(chip::OTAImageHeaderParser & parser, const char * otaFilePath, chip::OTAImageHeader & header)
MTROTAHeader * ParseOTAHeader(const char * otaFilePath)
{
uint8_t otaFileContent[kOtaHeaderMaxSize];
chip::ByteSpan buffer(otaFileContent);

std::ifstream otaFile(otaFilePath, std::ifstream::in);
if (!otaFile.is_open() || !otaFile.good()) {
ChipLogError(SoftwareUpdate, "Error opening OTA image file: %s", otaFilePath);
return false;
return nil;
}

otaFile.read(reinterpret_cast<char *>(otaFileContent), kOtaHeaderMaxSize);
if (otaFile.bad()) {
ChipLogError(SoftwareUpdate, "Error reading OTA image file: %s", otaFilePath);
return false;
}

parser.Init();
if (!parser.IsInitialized()) {
return false;
return nil;
}

CHIP_ERROR error = parser.AccumulateAndDecode(buffer, header);
if (error != CHIP_NO_ERROR) {
ChipLogError(SoftwareUpdate, "Error parsing OTA image header: %" CHIP_ERROR_FORMAT, error.Format());
return false;
}

return true;
NSError * error;
return [MTROTAHeaderParser headerFromData:[NSData dataWithBytes:buffer.data() length:buffer.size()] error:&error];
}

// Parses the JSON filepath and extracts DeviceSoftwareVersionModel parameters
Expand Down Expand Up @@ -138,11 +125,9 @@ static bool ParseJsonFileAndPopulateCandidates(
VerifyOrReturnError(ParseJsonFileAndPopulateCandidates(filePath, &candidates), CHIP_ERROR_INTERNAL);

for (DeviceSoftwareVersionModel * candidate : candidates) {
chip::OTAImageHeaderParser parser;
chip::OTAImageHeader header;

auto otaURL = [candidate.deviceModelData.otaURL UTF8String];
VerifyOrReturnError(ParseOTAHeader(parser, otaURL, header), CHIP_ERROR_INVALID_ARGUMENT);
auto header = ParseOTAHeader(otaURL);
VerifyOrReturnError(header != nil, CHIP_ERROR_INVALID_ARGUMENT);

ChipLogDetail(chipTool, "Validating image list candidate %s: ", [candidate.deviceModelData.otaURL UTF8String]);

Expand All @@ -154,23 +139,28 @@ static bool ParseJsonFileAndPopulateCandidates(
auto minApplicableSoftwareVersion = [candidate.deviceModelData.minApplicableSoftwareVersion unsignedLongValue];
auto maxApplicableSoftwareVersion = [candidate.deviceModelData.maxApplicableSoftwareVersion unsignedLongValue];

VerifyOrReturnError(vendorId == header.mVendorId, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(productId == header.mProductId, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(softwareVersion == header.mSoftwareVersion, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(softwareVersionStringLength == header.mSoftwareVersionString.size(), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(
memcmp(softwareVersionString, header.mSoftwareVersionString.data(), header.mSoftwareVersionString.size()) == 0,
auto headerVendorId = [header.vendorID unsignedIntValue];
auto headerProductId = [header.productID unsignedIntValue];
auto headerSoftwareVersion = [header.softwareVersion unsignedLongValue];
auto headerSoftwareVersionString = [header.softwareVersionString UTF8String];
auto headerSoftwareVersionStringLength = [header.softwareVersionString length];

VerifyOrReturnError(vendorId == headerVendorId, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(productId == headerProductId, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(softwareVersion == headerSoftwareVersion, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(softwareVersionStringLength == headerSoftwareVersionStringLength, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(memcmp(softwareVersionString, headerSoftwareVersionString, headerSoftwareVersionStringLength) == 0,
CHIP_ERROR_INVALID_ARGUMENT);

if (header.mMinApplicableVersion.HasValue()) {
VerifyOrReturnError(minApplicableSoftwareVersion == header.mMinApplicableVersion.Value(), CHIP_ERROR_INVALID_ARGUMENT);
if (header.minApplicableVersion) {
auto version = [header.minApplicableVersion unsignedLongValue];
VerifyOrReturnError(minApplicableSoftwareVersion == version, CHIP_ERROR_INVALID_ARGUMENT);
}

if (header.mMaxApplicableVersion.HasValue()) {
VerifyOrReturnError(maxApplicableSoftwareVersion == header.mMaxApplicableVersion.Value(), CHIP_ERROR_INVALID_ARGUMENT);
if (header.maxApplicableVersion) {
auto version = [header.maxApplicableVersion unsignedLongValue];
VerifyOrReturnError(maxApplicableSoftwareVersion == version, CHIP_ERROR_INVALID_ARGUMENT);
}

parser.Clear();
}

mOTADelegate.candidates = candidates;
Expand Down
1 change: 1 addition & 0 deletions src/darwin/Framework/CHIP/MTRError.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){
MTRErrorCodeWrongAddressType = 7,
MTRErrorCodeIntegrityCheckFailed = 8,
MTRErrorCodeTimeout = 9,
MTRErrorCodeBufferTooSmall = 10,
};
// clang-format on

Expand Down
6 changes: 6 additions & 0 deletions src/darwin/Framework/CHIP/MTRError.mm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ + (NSError *)errorForCHIPErrorCode:(CHIP_ERROR)errorCode
} else if (errorCode == CHIP_ERROR_TIMEOUT) {
code = MTRErrorCodeTimeout;
[userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Transaction timed out.", nil) }];
} else if (errorCode == CHIP_ERROR_BUFFER_TOO_SMALL) {
code = MTRErrorCodeBufferTooSmall;
[userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"A buffer is too small.", nil) }];
} else {
code = MTRErrorCodeGeneralError;
[userInfo addEntriesFromDictionary:@{
Expand Down Expand Up @@ -259,6 +262,9 @@ + (CHIP_ERROR)errorToCHIPErrorCode:(NSError * _Nullable)error
case MTRErrorCodeTimeout:
code = CHIP_ERROR_TIMEOUT.AsInteger();
break;
case MTRErrorCodeBufferTooSmall:
code = CHIP_ERROR_BUFFER_TOO_SMALL.AsInteger();
break;
case MTRErrorCodeGeneralError: {
if (error.userInfo != nil && error.userInfo[@"errorCode"] != nil) {
code = static_cast<decltype(code)>([error.userInfo[@"errorCode"] unsignedLongValue]);
Expand Down
56 changes: 56 additions & 0 deletions src/darwin/Framework/CHIP/MTROTAHeaderParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSUInteger, MTROTAImageDigestType) {
MTROTAImageDigestTypeSha256 = 1,
MTROTAImageDigestTypeSha256_128,
MTROTAImageDigestTypeSha256_120,
MTROTAImageDigestTypeSha256_96,
MTROTAImageDigestTypeSha256_64,
MTROTAImageDigestTypeSha256_32,
MTROTAImageDigestTypeSha384,
MTROTAImageDigestTypeSha512,
MTROTAImageDigestTypeSha3_224,
MTROTAImageDigestTypeSha3_256,
MTROTAImageDigestTypeSha3_384,
MTROTAImageDigestTypeSha3_512,
};

@interface MTROTAHeader : NSObject

@property (nonatomic, strong) NSNumber * vendorID;
@property (nonatomic, strong) NSNumber * productID;
@property (nonatomic, strong) NSNumber * payloadSize;
@property (nonatomic, strong) NSNumber * softwareVersion;
@property (nonatomic, strong) NSString * softwareVersionString;
@property (nonatomic, strong) NSString * releaseNotesURL;
@property (nonatomic, strong) NSData * imageDigest;
@property (nonatomic, assign) MTROTAImageDigestType imageDigestType;
@property (nonatomic, strong) NSNumber * minApplicableVersion;
@property (nonatomic, strong) NSNumber * maxApplicableVersion;

@end

@interface MTROTAHeaderParser : NSObject
+ (nullable MTROTAHeader *)headerFromData:(NSData *)data error:(NSError * __autoreleasing *)error;
@end

NS_ASSUME_NONNULL_END
72 changes: 72 additions & 0 deletions src/darwin/Framework/CHIP/MTROTAHeaderParser.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#import "MTROTAHeaderParser.h"

#import "MTRError.h"
#import "MTRError_Internal.h"
#import "NSDataSpanConversion.h"
#import "NSStringSpanConversion.h"

#include <lib/core/OTAImageHeader.h>

@implementation MTROTAHeader
@end

@implementation MTROTAHeaderParser
+ (nullable MTROTAHeader *)headerFromData:(NSData *)data error:(NSError * __autoreleasing *)error
{
chip::OTAImageHeaderParser parser;

parser.Init();

if (!parser.IsInitialized()) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
return nil;
}

chip::ByteSpan buffer = AsByteSpan(data);
chip::OTAImageHeader header;
CHIP_ERROR err = parser.AccumulateAndDecode(buffer, header);
if (err != CHIP_NO_ERROR) {
*error = [MTRError errorForCHIPErrorCode:err];
parser.Clear();
return nil;
}

auto headerObj = [MTROTAHeader new];
headerObj.vendorID = @(header.mVendorId);
headerObj.productID = @(header.mProductId);
headerObj.payloadSize = @(header.mPayloadSize);
headerObj.softwareVersion = @(header.mSoftwareVersion);
headerObj.softwareVersionString = AsString(header.mSoftwareVersionString);
headerObj.releaseNotesURL = AsString(header.mReleaseNotesURL);
headerObj.imageDigest = AsData(header.mImageDigest);
headerObj.imageDigestType = static_cast<MTROTAImageDigestType>(chip::to_underlying(header.mImageDigestType));

if (header.mMinApplicableVersion.HasValue()) {
headerObj.minApplicableVersion = @(header.mMinApplicableVersion.Value());
}

if (header.mMaxApplicableVersion.HasValue()) {
headerObj.maxApplicableVersion = @(header.mMaxApplicableVersion.Value());
}

parser.Clear();
return headerObj;
}
@end
1 change: 1 addition & 0 deletions src/darwin/Framework/CHIP/Matter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#import <Matter/MTRError.h>
#import <Matter/MTRKeypair.h>
#import <Matter/MTRManualSetupPayloadParser.h>
#import <Matter/MTROTAHeaderParser.h>
#import <Matter/MTROTAProviderDelegate.h>
#import <Matter/MTRPersistentStorageDelegate.h>
#import <Matter/MTRQRCodeSetupPayloadParser.h>
Expand Down
36 changes: 36 additions & 0 deletions src/darwin/Framework/CHIP/NSStringSpanConversion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#import "Foundation/Foundation.h"

#include <lib/support/Span.h>

NS_ASSUME_NONNULL_BEGIN

/**
* Utilities for converting between NSString and chip::CharSpan.
*/

inline chip::CharSpan AsCharSpan(NSString * str) { return chip::CharSpan([str UTF8String], [str length]); }

inline NSString * AsString(chip::CharSpan span)
{
return [[NSString alloc] initWithBytes:span.data() length:span.size() encoding:NSUTF8StringEncoding];
}

NS_ASSUME_NONNULL_END
8 changes: 8 additions & 0 deletions src/darwin/Framework/Matter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
1ED276E026C57CF000547A89 /* MTRCallbackBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1ED276DF26C57CF000547A89 /* MTRCallbackBridge.mm */; };
1ED276E226C5812A00547A89 /* MTRCluster.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1ED276E126C5812A00547A89 /* MTRCluster.mm */; };
1ED276E426C5832500547A89 /* MTRCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED276E326C5832500547A89 /* MTRCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
1EDCE545289049A100E41EC9 /* MTROTAHeaderParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EDCE543289049A100E41EC9 /* MTROTAHeaderParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
1EDCE546289049A100E41EC9 /* MTROTAHeaderParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1EDCE544289049A100E41EC9 /* MTROTAHeaderParser.mm */; };
27A53C1727FBC6920053F131 /* MTRAttestationTrustStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 27A53C1527FBC6920053F131 /* MTRAttestationTrustStoreBridge.h */; };
27A53C1827FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27A53C1627FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm */; };
2C1B027A2641DB4E00780EF1 /* MTROperationalCredentialsDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C1B02782641DB4E00780EF1 /* MTROperationalCredentialsDelegate.mm */; };
Expand Down Expand Up @@ -133,6 +135,8 @@
1ED276DF26C57CF000547A89 /* MTRCallbackBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MTRCallbackBridge.mm; path = "zap-generated/MTRCallbackBridge.mm"; sourceTree = "<group>"; };
1ED276E126C5812A00547A89 /* MTRCluster.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRCluster.mm; sourceTree = "<group>"; };
1ED276E326C5832500547A89 /* MTRCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRCluster.h; sourceTree = "<group>"; };
1EDCE543289049A100E41EC9 /* MTROTAHeaderParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTROTAHeaderParser.h; sourceTree = "<group>"; };
1EDCE544289049A100E41EC9 /* MTROTAHeaderParser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTROTAHeaderParser.mm; sourceTree = "<group>"; };
27A53C1527FBC6920053F131 /* MTRAttestationTrustStoreBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRAttestationTrustStoreBridge.h; sourceTree = "<group>"; };
27A53C1627FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAttestationTrustStoreBridge.mm; sourceTree = "<group>"; };
2C1B02782641DB4E00780EF1 /* MTROperationalCredentialsDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTROperationalCredentialsDelegate.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -313,6 +317,8 @@
B202528F2459E34F00F97062 /* CHIP */ = {
isa = PBXGroup;
children = (
1EDCE543289049A100E41EC9 /* MTROTAHeaderParser.h */,
1EDCE544289049A100E41EC9 /* MTROTAHeaderParser.mm */,
27A53C1527FBC6920053F131 /* MTRAttestationTrustStoreBridge.h */,
27A53C1627FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm */,
88EBF8CB27FABDD500686BC1 /* MTRDeviceAttestationDelegate.h */,
Expand Down Expand Up @@ -484,6 +490,7 @@
991DC08B247704DC00C13860 /* MTRLogging.h in Headers */,
5A7947E527C0129F00434CF2 /* MTRDeviceController+XPC.h in Headers */,
B2E0D7B4245B0B5C003C5B48 /* MTRError_Internal.h in Headers */,
1EDCE545289049A100E41EC9 /* MTROTAHeaderParser.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -623,6 +630,7 @@
1EC3238D271999E2002A8BF0 /* cluster-objects.cpp in Sources */,
991DC0892475F47D00C13860 /* MTRDeviceController.mm in Sources */,
B2E0D7B7245B0B5C003C5B48 /* MTRQRCodeSetupPayloadParser.mm in Sources */,
1EDCE546289049A100E41EC9 /* MTROTAHeaderParser.mm in Sources */,
1EC4CE5D25CC26E900D7304F /* MTRBaseClusters.mm in Sources */,
51E0310127EA20D20083DC9C /* MTRControllerAccessControl.mm in Sources */,
1ED276E226C5812A00547A89 /* MTRCluster.mm in Sources */,
Expand Down

0 comments on commit 2580913

Please sign in to comment.