Skip to content

Commit

Permalink
Apple: Added support for ECIES V3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
hvge committed Aug 8, 2024
1 parent 0abbc0f commit cc02414
Show file tree
Hide file tree
Showing 27 changed files with 1,091 additions and 142 deletions.
66 changes: 63 additions & 3 deletions proj-xcode/PowerAuth2.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions proj-xcode/PowerAuth2/PowerAuthSDK.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@
@return New instance of `PowerAuthCoreEciesEncryptor` object or nil if `PowerAuthConfiguration` contains an invalid data.
*/
- (nullable PowerAuthCoreEciesEncryptor*) eciesEncryptorForApplicationScope;
- (nullable PowerAuthCoreEciesEncryptor*) eciesEncryptorForApplicationScope PA2_DEPRECATED(1.9.0);

/**
Creates a new instance of ECIES encryptor suited for application's general end-to-end encryption purposes. The returned encryptor is
Expand All @@ -611,7 +611,11 @@
@return New instance of `PowerAuthCoreEciesEncryptor` object or nil if there's no valid activation.
*/
- (nullable PowerAuthCoreEciesEncryptor*) eciesEncryptorForActivationScope;
- (nullable PowerAuthCoreEciesEncryptor*) eciesEncryptorForActivationScope PA2_DEPRECATED(1.9.0);

- (nullable id<PowerAuthOperationTask>) eciesEncryptorForApplicationScopeWithCallback:(nonnull void(^)(PowerAuthCoreEciesEncryptor * _Nullable encryptor, NSError * _Nullable error))callback;

- (nullable id<PowerAuthOperationTask>) eciesEncryptorForActivationScopeWithCallback:(nonnull void(^)(PowerAuthCoreEciesEncryptor * _Nullable encryptor, NSError * _Nullable error))callback;

@end

Expand Down
190 changes: 76 additions & 114 deletions proj-xcode/PowerAuth2/PowerAuthSDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "PA2AsyncOperation.h"
#import "PA2ObjectSerialization.h"

#import "PA2KeystoreService.h"
#import "PA2TimeSynchronizationService.h"
#import "PA2PrivateTokenKeychainStore.h"
#import "PA2PrivateHttpTokenProvider.h"
Expand Down Expand Up @@ -61,6 +62,7 @@ @implementation PowerAuthSDK
PowerAuthKeychainConfiguration * _keychainConfiguration;
PowerAuthClientConfiguration * _clientConfiguration;

PA2KeystoreService * _keystoreService;
PA2TimeSynchronizationService * _timeSynchronizationService;
id<PowerAuthPrivateTokenStore> _tokenStore;
PA2HttpClient *_client;
Expand Down Expand Up @@ -269,10 +271,16 @@ - (NSString*) privateInstanceId
return _configuration.instanceId;
}

/**
This private method checks for valid PowerAuthCoreSessionSetup and throws a PowerAuthExceptionMissingConfig exception when the provided configuration
is not correct or is missing.
*/
- (id<PowerAuthCoreSessionProvider>) sessionProvider
{
return _sessionInterface;
}

- (id<PA2SessionInterface>) sessionInterface
{
return _sessionInterface; // same as "sessionProvider" but exposes private interfaces
}

- (void) checkForValidSetup
{
// This is OK to directly access _coreSession without a proper locking. Setup depends on runtime configuration,
Expand All @@ -284,6 +292,21 @@ - (void) checkForValidSetup

#pragma mark - Key management

- (PA2KeystoreService*) keystoreService
{
[_lock lock];
if (!_keystoreService) {
// Create keystore service
_keystoreService = [[PA2KeystoreService alloc] initWithHttpClient:_client
timeService:_timeSynchronizationService
deviceRelatedKey:[self deviceRelatedKey]
sessionSetup:_coreSession.sessionSetup
sharedLock:_lock];
}
[_lock unlock];
return _keystoreService;
}

- (NSData*) deviceRelatedKey
{
// Cache the possession key in the keychain
Expand Down Expand Up @@ -378,60 +401,6 @@ - (NSData*) biometryRelatedKeyWithAuthentication:(nonnull PowerAuthKeychainAuthe
#endif
}


- (PowerAuthCoreSignatureUnlockKeys*) signatureKeysForAuthentication:(nonnull PowerAuthAuthentication*)authentication
error:(NSError **)error
{
// Validate authentication object usage
[authentication validateUsage:NO];

// Generate signature key encryption keys
NSData *possessionKey = nil;
NSData *biometryKey = nil;
if (authentication.usePossession) {
if (authentication.overridenPossessionKey) {
possessionKey = authentication.overridenPossessionKey;
} else {
possessionKey = [self deviceRelatedKey];
}
}
if (authentication.useBiometry) {
if (authentication.overridenBiometryKey) {
// application specified a custom biometry key
biometryKey = authentication.overridenBiometryKey;
} else {
// default biometry key should be fetched
biometryKey = [self biometryRelatedKeyWithAuthentication:authentication.keychainAuthentication error:error];
if (!biometryKey) {
return nil;
}
}
}

// Prepare signature unlock keys structure
PowerAuthCoreSignatureUnlockKeys *keys = [[PowerAuthCoreSignatureUnlockKeys alloc] init];
keys.possessionUnlockKey = possessionKey;
keys.biometryUnlockKey = biometryKey;
keys.userPassword = authentication.password;
if (error) { *error = nil; }
return keys;
}

- (PowerAuthCoreSignatureFactor) determineSignatureFactorForAuthentication:(PowerAuthAuthentication*)authentication
{
PowerAuthCoreSignatureFactor factor = 0;
if (authentication.usePossession) {
factor |= PowerAuthCoreSignatureFactor_Possession;
}
if (authentication.password != nil) {
factor |= PowerAuthCoreSignatureFactor_Knowledge;
}
if (authentication.useBiometry) {
factor |= PowerAuthCoreSignatureFactor_Biometry;
}
return factor;
}

- (id<PowerAuthOperationTask>) fetchEncryptedVaultUnlockKey:(PowerAuthAuthentication*)authentication
reason:(PA2VaultUnlockReason)reason
callback:(void(^)(NSString * encryptedEncryptionKey, NSError *error))callback
Expand Down Expand Up @@ -540,11 +509,6 @@ - (BOOL) hasPendingProtocolUpgrade
return _sessionInterface.hasPendingProtocolUpgrade;
}

- (id<PowerAuthCoreSessionProvider>) sessionProvider
{
return _sessionInterface;
}

- (void) cancelAllPendingTasks
{
[_getActivationStatusTask cancel];
Expand Down Expand Up @@ -1075,46 +1039,6 @@ - (NSString*) offlineSignatureWithAuthentication:(PowerAuthAuthentication*)authe
}


/**
This private method implements both online & offline signature calculations. Unlike the public interfaces, method accepts
PA2HTTPRequestData object as a source for data for signing and returns structured PA2HTTPRequestDataSignature object.
*/
- (PowerAuthCoreHTTPRequestDataSignature*) signHttpRequestData:(PowerAuthCoreHTTPRequestData*)requestData
authentication:(PowerAuthAuthentication*)authentication
error:(NSError**)error
{
[self checkForValidSetup];

return [[_sessionInterface writeTaskWithSession:^PA2Result<PowerAuthCoreHTTPRequestDataSignature*>* (PowerAuthCoreSession * session) {
// Check if there is an activation present
if (!session.hasValidActivation) {
return [PA2Result failure:PA2MakeError(PowerAuthErrorCode_MissingActivation, nil)];
}

// Determine authentication factor type
PowerAuthCoreSignatureFactor factor = [self determineSignatureFactorForAuthentication:authentication];
if (factor == 0) {
return [PA2Result failure:PA2MakeError(PowerAuthErrorCode_WrongParameter, nil)];
}

// Generate signature key encryption keys
NSError * localError = nil;
PowerAuthCoreSignatureUnlockKeys *keys = [self signatureKeysForAuthentication:authentication error:&localError];
if (keys == nil) { // Unable to fetch Touch ID related record - maybe user or iOS canacelled the operation?
return [PA2Result failure:localError];
}

// Compute signature for provided values and return result.
PowerAuthCoreHTTPRequestDataSignature * signature = [session signHttpRequestData:requestData keys:keys factor:factor];
if (signature == nil) {
return [PA2Result failure:PA2MakeError(PowerAuthErrorCode_SignatureError, nil)];
}
return [PA2Result success:signature];

}] extractResult:error];
}


- (BOOL) verifyServerSignedData:(nonnull NSData*)data
signature:(nonnull NSString*)signature
masterKey:(BOOL)masterKey
Expand Down Expand Up @@ -1526,25 +1450,63 @@ - (NSData*) generateInvalidBiometricKey

@implementation PowerAuthSDK (E2EE)

- (PowerAuthCoreEciesEncryptor*) eciesEncryptorForApplicationScope
- (id<PowerAuthOperationTask>) eciesEncryptorForApplicationScopeWithCallback:(void (^)(PowerAuthCoreEciesEncryptor *, NSError *))callback
{
PA2PrivateEncryptorFactory * factory = [[PA2PrivateEncryptorFactory alloc] initWithSessionProvider:_sessionInterface deviceRelatedKey:nil];
return [factory encryptorWithId:PA2EncryptorId_GenericApplicationScope error:nil];
return [self eciesEncryptorWithScope:PowerAuthCoreEciesEncryptorScope_Application callback:callback];
}

- (PowerAuthCoreEciesEncryptor*) eciesEncryptorForActivationScope
- (id<PowerAuthOperationTask>) eciesEncryptorForActivationScopeWithCallback:(void (^)(PowerAuthCoreEciesEncryptor *, NSError *))callback
{
return [_sessionInterface readTaskWithSession:^id (PowerAuthCoreSession * session) {
if (!session.hasValidActivation) {
PowerAuthLog(@"eciesEncryptorForActivation: There's no activation.");
return nil;
return [self eciesEncryptorWithScope:PowerAuthCoreEciesEncryptorScope_Activation callback:callback];
}

// Private

- (id<PowerAuthOperationTask>) eciesEncryptorWithScope:(PowerAuthCoreEciesEncryptorScope)scope
callback:(void (^)(PowerAuthCoreEciesEncryptor *, NSError *))callback
{
return [_keystoreService createKeyForEncryptorScope:scope callback:^(NSError * error) {
PowerAuthCoreEciesEncryptor * encryptor;
if (!error) {
encryptor = [self eciesEncryptorWithScope:scope error:&error];
} else {
encryptor = nil;
}
NSData * deviceKey = [self deviceRelatedKey];
PA2PrivateEncryptorFactory * factory = [[PA2PrivateEncryptorFactory alloc] initWithSessionProvider:_sessionInterface deviceRelatedKey:deviceKey];
return [factory encryptorWithId:PA2EncryptorId_GenericActivationScope error:nil];
callback(encryptor, error);
}];
}

- (PowerAuthCoreEciesEncryptor*) eciesEncryptorWithScope:(PowerAuthCoreEciesEncryptorScope)scope error:(NSError**)error
{
if (scope == PowerAuthCoreEciesEncryptorScope_Activation) {
return [[_sessionInterface readTaskWithSession:^PA2Result*(PowerAuthCoreSession * session) {
if (!session.hasValidActivation) {
return [PA2Result failure:PA2MakeError(PowerAuthErrorCode_MissingActivation, nil)];
}
NSError * error = nil;
NSData * deviceKey = [self deviceRelatedKey];
PA2PrivateEncryptorFactory * factory = [[PA2PrivateEncryptorFactory alloc] initWithSessionProvider:_sessionInterface deviceRelatedKey:deviceKey];
PowerAuthCoreEciesEncryptor * encryptor = [factory encryptorWithId:PA2EncryptorId_GenericActivationScope error:&error];
return [PA2Result success:encryptor orFailure:error];
}] extractResult:error];
} else {
PA2PrivateEncryptorFactory * factory = [[PA2PrivateEncryptorFactory alloc] initWithSessionProvider:_sessionInterface deviceRelatedKey:nil];
return [factory encryptorWithId:PA2EncryptorId_GenericApplicationScope error:error];
}
}

// PA2_DEPRECATED(1.9.0)
- (PowerAuthCoreEciesEncryptor*) eciesEncryptorForApplicationScope
{
return [self eciesEncryptorWithScope:PowerAuthCoreEciesEncryptorScope_Application error:nil];
}

// PA2_DEPRECATED(1.9.0)
- (PowerAuthCoreEciesEncryptor*) eciesEncryptorForActivationScope
{
return [self eciesEncryptorWithScope:PowerAuthCoreEciesEncryptorScope_Activation error:nil];
}

@end


Expand Down
25 changes: 25 additions & 0 deletions proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 Wultra s.r.o.
*
* 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 "PA2Codable.h"

@interface PA2GetTemporaryKeyRequest : NSObject<PA2Encodable>

@property (nonatomic, strong) NSString * appKey;
@property (nonatomic, strong) NSString * activationId;
@property (nonatomic, strong) NSString * challenge;

@end
30 changes: 30 additions & 0 deletions proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024 Wultra s.r.o.
*
* 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 "PA2GetTemporaryKeyRequest.h"

@implementation PA2GetTemporaryKeyRequest

- (NSDictionary<NSString *,NSObject *> *)toDictionary
{
if (_activationId) {
return @{ @"appKey" : _appKey, @"activationId": _activationId, @"challenge" : _challenge };
} else {
return @{ @"appKey" : _appKey, @"challenge" : _challenge };
}
}

@end
29 changes: 29 additions & 0 deletions proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 Wultra s.r.o.
*
* 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 "PA2Codable.h"

@interface PA2GetTemporaryKeyResponse : NSObject<PA2Decodable>

@property (nonatomic, strong) NSString * appKey;
@property (nonatomic, strong) NSString * activationId;
@property (nonatomic, strong) NSString * challenge;
@property (nonatomic, strong) NSString * keyId;
@property (nonatomic, strong) NSString * publicKey;
@property (nonatomic, assign) UInt64 expiration;
@property (nonatomic, assign) UInt64 serverTime;

@end
Loading

0 comments on commit cc02414

Please sign in to comment.