blob: c4694884ea829dd2d7ae0c04dab7a0fc10bd5228 [file] [log] [blame]
/**
*
* Copyright (c) 2020-2024 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 "MTRSetupPayload_Internal.h"
#import "MTRError_Internal.h"
#import "MTRFramework.h"
#import "MTRLogging_Internal.h"
#import "MTRUtilities.h"
#include <lib/support/SafeInt.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/ManualSetupPayloadParser.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadParser.h>
#include <setup_payload/SetupPayload.h>
#include <string>
MTR_DIRECT_MEMBERS
@implementation MTROptionalQRCodeInfo {
chip::OptionalQRCodeInfo _info;
}
static uint8_t ValidateVendorTag(NSNumber * tag)
{
auto integerValue = tag.integerValue;
auto tagValue = static_cast<uint8_t>(integerValue);
MTRVerifyArgumentOrDie(tag != nil && chip::CanCastTo<uint8_t>(integerValue) && chip::SetupPayload::IsVendorTag(tagValue), @"tag must be a vendor tag (0x80 - 0xFF)");
return tagValue;
}
- (instancetype)initWithTag:(NSNumber *)tag int32Value:(int32_t)value
{
self = [super init];
_info.type = chip::optionalQRCodeInfoTypeInt32;
_info.tag = ValidateVendorTag(tag);
_info.int32 = value;
return self;
}
- (instancetype)initWithTag:(NSNumber *)tag stringValue:(NSString *)value
{
self = [super init];
_info.type = chip::optionalQRCodeInfoTypeString;
_info.tag = ValidateVendorTag(tag);
MTRVerifyArgumentOrDie(value != nil, @"value");
_info.data = value.UTF8String;
return self;
}
- (nullable instancetype)initWithQRCodeInfo:(chip::OptionalQRCodeInfo const &)info
{
self = [super init];
_info = info;
// Don't expose objects with an out-of-range tag or invalid type
VerifyOrReturnValue(chip::SetupPayload::IsVendorTag(_info.tag), nil);
VerifyOrReturnValue(self.type != MTROptionalQRCodeInfoTypeUnknown, nil);
return self;
}
- (CHIP_ERROR)addAsVendorElementTo:(chip::SetupPayload &)payload
{
switch (_info.type) {
case chip::optionalQRCodeInfoTypeString:
return payload.addOptionalVendorData(_info.tag, _info.data);
case chip::optionalQRCodeInfoTypeInt32:
return payload.addOptionalVendorData(_info.tag, _info.int32);
default:
return CHIP_ERROR_INCORRECT_STATE;
}
}
- (MTROptionalQRCodeInfoType)type
{
// The way OptionalQRCodeInfo uses what are effectively C type identifiers (uint32 etc)
// rather than TLV types is not ideal. If we add support for additional integer types
// we should consider replacing MTROptionalQRCodeInfoTypeInt32 with
// MTROptionalQRCodeInfoTypeInteger and hiding the low-level C representation.
switch (_info.type) {
case chip::optionalQRCodeInfoTypeString:
return MTROptionalQRCodeInfoTypeString;
case chip::optionalQRCodeInfoTypeInt32:
return MTROptionalQRCodeInfoTypeInt32;
// No 'default:' so we get a warning if new types are added.
// Note: isEqual: also switches over these types.
// OptionalQRCodeInfo does not support these types
case chip::optionalQRCodeInfoTypeInt64:
case chip::optionalQRCodeInfoTypeUInt32:
case chip::optionalQRCodeInfoTypeUInt64:
// We should never see the unknown type
case chip::optionalQRCodeInfoTypeUnknown:
/* fall through */;
}
return MTROptionalQRCodeInfoTypeUnknown;
}
- (NSNumber *)tag
{
return @(_info.tag);
}
- (NSNumber *)integerValue
{
VerifyOrReturnValue(_info.type == chip::optionalQRCodeInfoTypeInt32, nil);
return @(_info.int32);
}
- (NSString *)stringValue
{
VerifyOrReturnValue(_info.type == chip::optionalQRCodeInfoTypeString, nil);
return [NSString stringWithUTF8String:_info.data.c_str()];
}
- (id)copyWithZone:(NSZone *)zone
{
return self; // immutable
}
- (NSUInteger)hash
{
return _info.type << 8 | _info.tag;
}
- (BOOL)isEqual:(id)object
{
VerifyOrReturnValue([object class] == [self class], NO);
MTROptionalQRCodeInfo * other = object;
VerifyOrReturnValue(_info.tag == other->_info.tag, NO);
VerifyOrReturnValue(_info.type == other->_info.type, NO);
switch (_info.type) {
case chip::optionalQRCodeInfoTypeString:
return _info.data == other->_info.data;
case chip::optionalQRCodeInfoTypeInt32:
return _info.int32 == other->_info.int32;
default:
return NO; // unreachable, type is checked in init
}
}
- (NSString *)description
{
return [NSString stringWithFormat:@"0x%02x=%@", self.tag.unsignedCharValue, self.integerValue ?: self.stringValue];
}
@end
static constexpr uint8_t kShortDiscriminatorMask = (1 << chip::SetupDiscriminator::kShortBits) - 1;
static constexpr uint16_t kLongDiscriminatorMask = (1 << chip::SetupDiscriminator::kLongBits) - 1;
static MTRDiscoveryCapabilities KnownDiscoveryCapabilities(NSUInteger value)
{
// A known but 0 value cannot be represented by MTRDiscoveryCapabilities
// since MTRDiscoveryCapabilitiesUnknown represents an absent value.
// However such a 0 value is not logically valid, because OnNetwork must
// always be supported. So just return that if we get a value of 0.
return (value != MTRDiscoveryCapabilitiesUnknown ? value : MTRDiscoveryCapabilitiesOnNetwork);
}
static NSString * MTRDiscoveryCapabilitiesAsString(MTRDiscoveryCapabilities value)
{
VerifyOrReturnValue(value != MTRDiscoveryCapabilitiesUnknown, @"Unknown");
NSMutableString * capabilities = [NSMutableString string];
if (value & MTRDiscoveryCapabilitiesSoftAP) {
[capabilities appendString:@"|SoftAP"];
value &= ~MTRDiscoveryCapabilitiesSoftAP;
}
if (value & MTRDiscoveryCapabilitiesBLE) {
[capabilities appendString:@"|BLE"];
value &= ~MTRDiscoveryCapabilitiesBLE;
}
if (value & MTRDiscoveryCapabilitiesOnNetwork) {
[capabilities appendString:@"|OnNetwork"];
value &= ~MTRDiscoveryCapabilitiesOnNetwork;
}
if (value != 0) {
[capabilities appendFormat:@"|0x%llx", (unsigned long long) value];
}
return [capabilities substringFromIndex:1];
}
MTR_DIRECT_MEMBERS
@implementation MTRSetupPayload {
// Apart from the additional logic to handle discriminators detailed below,
// this class is simply a wrapper around this underlying SetupPayload.
chip::SetupPayload _payload;
// SetupPayload deals with the discriminator value and its shortness as a composite
// SetupDiscriminator value, which is arguably a tidier API than letting clients
// set a value first and only later tell us whether it's long or short. But that is
// what our API does, so we need to continue to support it. To make this work, we
// keep track of the potentially-long value that was set by the client.
NSNumber * _Nullable _shadowDiscriminator;
}
+ (void)initialize
{
// Some aspects of working with chip::SetupPayload use Platform memory primitives.
MTRFrameworkInit();
}
- (nullable instancetype)initWithPayload:(NSString *)payload
{
return ([payload hasPrefix:@"MT:"]) ? [self initWithQRCode:payload] : [self initWithManualPairingCode:payload];
}
- (CHIP_ERROR)initializeFromQRCode:(NSString *)qrCode
{
std::string string([(qrCode ?: @"") UTF8String]); // handle nil gracefully
chip::QRCodeSetupPayloadParser parser(string);
return parser.populatePayload(_payload);
}
- (nullable instancetype)initWithQRCode:(NSString *)qrCodePayload
{
self = [super init];
CHIP_ERROR err = [self initializeFromQRCode:qrCodePayload];
if (err != CHIP_NO_ERROR) {
MTR_LOG_ERROR("Failed to parse QR Code payload: %" CHIP_ERROR_FORMAT, err.Format());
return nil;
}
if (!_payload.isValidQRCodePayload(chip::PayloadContents::ValidationMode::kConsume)) {
MTR_LOG_ERROR("Invalid QR Code payload");
return nil;
}
return self;
}
- (nullable instancetype)initWithManualPairingCode:(NSString *)manualCode
{
self = [super init];
std::string string([(manualCode ?: @"") UTF8String]); // handle nil gracefully
chip::ManualSetupPayloadParser parser(string);
CHIP_ERROR err = parser.populatePayload(_payload);
if (err != CHIP_NO_ERROR) {
MTR_LOG_ERROR("Failed to parse Manual Pairing Code: %" CHIP_ERROR_FORMAT, err.Format());
return nil;
}
if (!_payload.isValidManualCode(chip::PayloadContents::ValidationMode::kConsume)) {
MTR_LOG_ERROR("Invalid Manual Pairing Code");
return nil;
}
return self;
}
- (instancetype)initWithSetupPasscode:(NSNumber *)setupPasscode discriminator:(NSNumber *)discriminator
{
self = [super init];
// A default-constructed SetupPayload already has most of the expected
// default values. Set discoveryCapabilities to "all known methods" so
// we're representing a valid QR Code payload.
self.setupPasscode = setupPasscode;
self.discriminator = discriminator;
self.discoveryCapabilities = MTRDiscoveryCapabilitiesAllMask;
return self;
}
- (instancetype)initWithSetupPayload:(chip::SetupPayload)setupPayload
{
self = [super init];
_payload = setupPayload;
return self;
}
#pragma mark - Mutable properties
- (NSNumber *)version
{
return @(_payload.version);
}
- (void)setVersion:(NSNumber *)version
{
_payload.version = static_cast<decltype(_payload.version)>(version.unsignedIntegerValue);
}
- (NSNumber *)vendorID
{
return @(_payload.vendorID);
}
- (void)setVendorID:(NSNumber *)vendorID
{
_payload.vendorID = static_cast<decltype(_payload.vendorID)>(vendorID.unsignedIntegerValue);
}
- (NSNumber *)productID
{
return @(_payload.productID);
}
- (void)setProductID:(NSNumber *)productID
{
_payload.productID = static_cast<decltype(_payload.productID)>(productID.unsignedIntegerValue);
}
- (MTRCommissioningFlow)commissioningFlow
{
// To avoid ending up with special logic to handle MTRCommissioningFlowInvalid,
// we simply cast between MTRCommissioningFlow and chip::CommissioningFlow.
// Both types represent the same set of values defined in the Matter spec.
using chip::CommissioningFlow;
static_assert(static_cast<CommissioningFlow>(MTRCommissioningFlowStandard) == CommissioningFlow::kStandard);
static_assert(static_cast<CommissioningFlow>(MTRCommissioningFlowUserActionRequired) == CommissioningFlow::kUserActionRequired);
static_assert(static_cast<CommissioningFlow>(MTRCommissioningFlowCustom) == CommissioningFlow::kCustom);
return static_cast<MTRCommissioningFlow>(_payload.commissioningFlow);
}
- (void)setCommissioningFlow:(MTRCommissioningFlow)commissioningFlow
{
_payload.commissioningFlow = static_cast<chip::CommissioningFlow>(commissioningFlow);
}
- (MTRDiscoveryCapabilities)discoveryCapabilities
{
VerifyOrReturnValue(_payload.rendezvousInformation.HasValue(), MTRDiscoveryCapabilitiesUnknown);
// MTRDiscoveryCapabilities and chip::RendezvousInformationFlag represent
// the same set of bit flags from the Matter spec, so we can simply cast.
using RendezvousFlag = chip::RendezvousInformationFlag;
static_assert(static_cast<RendezvousFlag>(MTRDiscoveryCapabilitiesSoftAP) == RendezvousFlag::kSoftAP);
static_assert(static_cast<RendezvousFlag>(MTRDiscoveryCapabilitiesBLE) == RendezvousFlag::kBLE);
static_assert(static_cast<RendezvousFlag>(MTRDiscoveryCapabilitiesOnNetwork) == RendezvousFlag::kOnNetwork);
auto value = static_cast<MTRDiscoveryCapabilities>(_payload.rendezvousInformation.Value().Raw());
// Ensure a known (HasValue()) but 0 value does not map to MTRDiscoveryCapabilitiesUnknown.
return KnownDiscoveryCapabilities(value);
}
- (void)setDiscoveryCapabilities:(MTRDiscoveryCapabilities)discoveryCapabilities
{
if (discoveryCapabilities == MTRDiscoveryCapabilitiesUnknown) {
_payload.rendezvousInformation.ClearValue();
} else {
auto flags = static_cast<chip::RendezvousInformationFlags::IntegerType>(discoveryCapabilities);
_payload.rendezvousInformation.SetValue(chip::RendezvousInformationFlags(flags));
}
}
- (NSNumber *)discriminator
{
VerifyOrReturnValue(_shadowDiscriminator == nil, _shadowDiscriminator);
auto & pd = _payload.discriminator;
return @(pd.IsShortDiscriminator() ? pd.GetShortValue() : pd.GetLongValue());
}
- (void)setDiscriminator:(uint16_t)discriminator isShort:(BOOL)isShort
{
if (isShort) {
_payload.discriminator.SetShortValue(discriminator & kShortDiscriminatorMask);
_shadowDiscriminator = @(discriminator); // keep as a shadow value
} else {
_payload.discriminator.SetLongValue(discriminator);
_shadowDiscriminator = nil; // no need to keep a shadow copy
}
}
- (void)setDiscriminator:(NSNumber *)discriminator
{
// Truncate down to the range of a long discriminator. Then update
// our SetupPayload with the value, but keep the current shortness.
uint16_t value = discriminator.unsignedIntegerValue & kLongDiscriminatorMask;
[self setDiscriminator:value isShort:self.hasShortDiscriminator];
}
- (BOOL)hasShortDiscriminator
{
return _payload.discriminator.IsShortDiscriminator();
}
- (void)setHasShortDiscriminator:(BOOL)hasShortDiscriminator
{
VerifyOrReturn(hasShortDiscriminator != self.hasShortDiscriminator);
[self setDiscriminator:self.discriminator.unsignedShortValue isShort:hasShortDiscriminator];
}
- (NSNumber *)setupPasscode
{
return @(_payload.setUpPINCode);
}
- (void)setSetupPasscode:(NSNumber *)setupPasscode
{
_payload.setUpPINCode = static_cast<decltype(_payload.setUpPINCode)>(setupPasscode.unsignedIntegerValue);
}
- (nullable NSString *)serialNumber
{
std::string value;
VerifyOrReturnValue(_payload.getSerialNumber(value) == CHIP_NO_ERROR, nil);
return [NSString stringWithUTF8String:value.c_str()];
}
- (void)setSerialNumber:(nullable NSString *)serialNumber
{
if (serialNumber) {
NSString * existing = self.serialNumber;
if (existing) {
// The underlying TLV tag can be encoded as either a string or an integer,
// avoid changing it if the represented serial number is not changing.
VerifyOrReturn(![existing isEqualToString:serialNumber]);
_payload.removeSerialNumber();
}
CHIP_ERROR err = _payload.addSerialNumber(serialNumber.UTF8String);
if (err != CHIP_NO_ERROR) {
MTR_LOG_ERROR("Ignoring unexpected error in SetupPayload::addSerialNumber: %" CHIP_ERROR_FORMAT, err.Format());
}
} else {
_payload.removeSerialNumber();
}
}
- (NSArray<MTROptionalQRCodeInfo *> *)vendorElements
{
std::vector<chip::OptionalQRCodeInfo> elements = _payload.getAllOptionalVendorData();
VerifyOrReturnValue(!elements.empty(), @[]);
NSMutableArray<MTROptionalQRCodeInfo *> * infos = [[NSMutableArray alloc] initWithCapacity:elements.size()];
for (auto const & element : elements) {
MTROptionalQRCodeInfo * info = [[MTROptionalQRCodeInfo alloc] initWithQRCodeInfo:element];
if (info != nil) { // ignore invalid elements (there shouldn't be any)
[infos addObject:info];
}
}
return infos;
}
- (MTROptionalQRCodeInfo *)vendorElementWithTag:(NSNumber *)tag
{
chip::OptionalQRCodeInfo element;
VerifyOrReturnValue(_payload.getOptionalVendorData(ValidateVendorTag(tag), element) == CHIP_NO_ERROR, nil);
return [[MTROptionalQRCodeInfo alloc] initWithQRCodeInfo:element];
}
- (void)removeVendorElementWithTag:(NSNumber *)tag
{
_payload.removeOptionalVendorData(ValidateVendorTag(tag));
}
- (void)addOrReplaceVendorElement:(MTROptionalQRCodeInfo *)element
{
MTRVerifyArgumentOrDie(element != nil, @"element");
CHIP_ERROR err = [element addAsVendorElementTo:_payload];
VerifyOrDieWithMsg(err == CHIP_NO_ERROR, NotSpecified, "Internal error: %" CHIP_ERROR_FORMAT, err.Format());
}
#pragma mark - Export methods
- (nullable NSString *)manualEntryCode
{
chip::ManualSetupPayloadGenerator generator(_payload);
std::string result;
VerifyOrReturnValue(generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR, nil);
return [NSString stringWithUTF8String:result.c_str()];
}
- (nullable NSString *)qrCodeString
{
return [self qrCodeStringSkippingValidation:NO];
}
- (nullable NSString *)qrCodeStringSkippingValidation:(BOOL)allowInvalid
{
chip::QRCodeSetupPayloadGenerator generator(_payload);
generator.SetAllowInvalidPayload(allowInvalid);
std::string result;
CHIP_ERROR err = generator.payloadBase38RepresentationWithAutoTLVBuffer(result);
if (allowInvalid) {
// Encoding should always work if invalid payloads are allowed
VerifyOrDieWithMsg(err == CHIP_NO_ERROR, NotSpecified, "Internal error: %" CHIP_ERROR_FORMAT, err.Format());
} else {
VerifyOrReturnValue(err == CHIP_NO_ERROR, nil);
}
return [NSString stringWithUTF8String:result.c_str()];
}
#pragma mark - Miscellaneous
- (id)copyWithZone:(NSZone *)zone
{
MTRSetupPayload * copy = [[MTRSetupPayload alloc] initWithSetupPayload:_payload];
copy->_shadowDiscriminator = _shadowDiscriminator;
return copy;
}
- (NSUInteger)hash
{
return self.discriminator.unsignedIntegerValue;
}
- (BOOL)isEqual:(id)object
{
VerifyOrReturnValue([object class] == [self class], NO);
MTRSetupPayload * other = object;
VerifyOrReturnValue(_payload == other->_payload, NO);
VerifyOrReturnValue(MTREqualObjects(_shadowDiscriminator, other->_shadowDiscriminator), NO);
return YES;
}
- (NSString *)description
{
// Note: The description does not include the passcode for security reasons!
NSMutableString * result = [NSMutableString stringWithFormat:@"<MTRSetupPayload: discriminator=0x%0*x",
(self.hasShortDiscriminator ? 1 : 3), self.discriminator.unsignedIntValue];
auto capabilities = self.discoveryCapabilities;
if (capabilities != MTRDiscoveryCapabilitiesUnknown) {
[result appendFormat:@" discoveryCapabilities=%@", MTRDiscoveryCapabilitiesAsString(capabilities)];
}
auto flow = self.commissioningFlow;
if (flow != MTRCommissioningFlowStandard) {
[result appendFormat:@" commissioningFlow=0x%llx", (unsigned long long) flow];
}
[result appendString:@">"];
return result;
}
#pragma mark - NSSecureCoding
static NSString * const MTRSetupPayloadCodingKeyVersion = @"MTRSP.ck.version";
static NSString * const MTRSetupPayloadCodingKeyVendorID = @"MTRSP.ck.vendorID";
static NSString * const MTRSetupPayloadCodingKeyProductID = @"MTRSP.ck.productID";
static NSString * const MTRSetupPayloadCodingKeyCommissioningFlow = @"MTRSP.ck.commissioningFlow";
static NSString * const MTRSetupPayloadCodingKeyRendevouzInformation = @"MTRSP.ck.rendezvousFlags";
static NSString * const MTRSetupPayloadCodingKeyHasShortDiscriminator = @"MTRSP.ck.hasShortDiscriminator";
static NSString * const MTRSetupPayloadCodingKeyDiscriminator = @"MTRSP.ck.discriminator";
static NSString * const MTRSetupPayloadCodingKeySetupPasscode = @"MTRSP.ck.setupPINCode";
static NSString * const MTRSetupPayloadCodingKeySerialNumber = @"MTRSP.ck.serialNumber";
static NSString * const MTRSetupPayloadCodingKeyQRCode = @"qr";
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:[self qrCodeStringSkippingValidation:YES] forKey:MTRSetupPayloadCodingKeyQRCode];
[coder encodeObject:self.version forKey:MTRSetupPayloadCodingKeyVersion];
[coder encodeObject:self.vendorID forKey:MTRSetupPayloadCodingKeyVendorID];
[coder encodeObject:self.productID forKey:MTRSetupPayloadCodingKeyProductID];
[coder encodeInteger:self.commissioningFlow forKey:MTRSetupPayloadCodingKeyCommissioningFlow];
// For compatibility reasons, keep encoding rendevouzInformation instead of discoveryCapabilities
[coder encodeObject:self.rendezvousInformation forKey:MTRSetupPayloadCodingKeyRendevouzInformation];
[coder encodeInteger:(self.hasShortDiscriminator ? 1 : 0) forKey:MTRSetupPayloadCodingKeyHasShortDiscriminator];
[coder encodeObject:self.discriminator forKey:MTRSetupPayloadCodingKeyDiscriminator];
[coder encodeObject:self.setupPasscode forKey:MTRSetupPayloadCodingKeySetupPasscode];
[coder encodeObject:self.serialNumber forKey:MTRSetupPayloadCodingKeySerialNumber];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
// We can't rely on the QR code to be present because older versions of this class
// did not encode it. When present, it carries almost the entire state of the object.
NSString * qrCode = [coder decodeObjectOfClass:NSString.class forKey:MTRSetupPayloadCodingKeyQRCode];
if (qrCode != nil) {
[self initializeFromQRCode:qrCode];
} else {
self.version = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeyVersion];
self.vendorID = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeyVendorID];
self.productID = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeyProductID];
self.commissioningFlow = static_cast<MTRCommissioningFlow>([coder decodeIntegerForKey:MTRSetupPayloadCodingKeyCommissioningFlow]);
self.setupPasscode = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeySetupPasscode];
self.serialNumber = [coder decodeObjectOfClass:NSString.class forKey:MTRSetupPayloadCodingKeySerialNumber];
}
// The QR code cannot represent short discriminators or the absence of rendevouz
// information, so always decode the state of those properties separately.
self.hasShortDiscriminator = ([coder decodeIntegerForKey:MTRSetupPayloadCodingKeyHasShortDiscriminator] != 0);
self.discriminator = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeyDiscriminator];
// For compatibility reasons, keep decoding rendevouzInformation instead of discoveryCapabilities
self.rendezvousInformation = [coder decodeObjectOfClass:NSNumber.class forKey:MTRSetupPayloadCodingKeyRendevouzInformation];
return self;
}
#pragma mark - Utility class methods
+ (NSUInteger)generateRandomPIN
{
do {
// Make sure the thing we generate is in the right range.
uint32_t setupPIN = arc4random_uniform(chip::kSetupPINCodeMaximumValue) + 1;
if (chip::SetupPayload::IsValidSetupPIN(setupPIN)) {
return setupPIN;
}
// We got pretty unlucky with our random number generation. Just try
// again. The chance that this loop does not terminate in a reasonable
// amount of time is astronomically low, assuming arc4random_uniform is not
// broken.
} while (true);
}
+ (NSNumber *)generateRandomSetupPasscode
{
return @([self generateRandomPIN]);
}
@end
MTR_DIRECT_MEMBERS
@implementation MTROptionalQRCodeInfo (Deprecated)
- (instancetype)init
{
return [self initWithTag:@0xff stringValue:@""];
}
- (void)setType:(MTROptionalQRCodeInfoType)type
{
/* ignored */
}
- (void)setTag:(NSNumber *)tag
{
/* ignored */
}
- (NSNumber *)infoType
{
return @(self.type);
}
- (void)setInfoType:(NSNumber *)infoType
{
/* ignored */
}
- (void)setIntegerValue:(NSNumber *)integerValue
{
/* ignored */
}
- (void)setStringValue:(NSString *)stringValue
{
/* ignored */
}
@end
MTR_DIRECT_MEMBERS
@implementation MTRSetupPayload (Deprecated)
- (instancetype)init
{
return [super init]; // a default-constructed SetupPayload is fine here
}
+ (instancetype)new
{
return [super new];
}
+ (MTRSetupPayload * _Nullable)setupPayloadWithOnboardingPayload:(NSString *)onboardingPayload
error:(NSError * __autoreleasing *)error
{
MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithPayload:onboardingPayload];
if (!payload && error) {
*error = [MTRError errorWithCode:MTRErrorCodeInvalidArgument];
}
return payload;
}
- (nullable NSNumber *)rendezvousInformation
{
auto value = self.discoveryCapabilities;
return (value != MTRDiscoveryCapabilitiesUnknown) ? @(value) : nil;
}
- (void)setRendezvousInformation:(nullable NSNumber *)rendezvousInformation
{
if (rendezvousInformation != nil) {
self.discoveryCapabilities = KnownDiscoveryCapabilities(rendezvousInformation.unsignedIntegerValue);
} else {
self.discoveryCapabilities = MTRDiscoveryCapabilitiesUnknown;
}
}
- (NSNumber *)setUpPINCode
{
return self.setupPasscode;
}
- (void)setSetUpPINCode:(NSNumber *)setUpPINCode
{
self.setupPasscode = setUpPINCode;
}
- (nullable NSString *)qrCodeString:(NSError * __autoreleasing _Nullable *)error
{
NSString * result = [self qrCodeString];
if (!result && error) {
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE];
}
return result;
}
- (NSArray<MTROptionalQRCodeInfo *> *)getAllOptionalVendorData:(NSError * __autoreleasing *)error
{
return self.vendorElements; // never actually fails
}
@end