From cc02414eee1d20076598c56c88d704a8d8788316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Thu, 8 Aug 2024 12:26:37 +0200 Subject: [PATCH] Apple: Added support for ECIES V3.3 --- .../PowerAuth2.xcodeproj/project.pbxproj | 66 +++++- proj-xcode/PowerAuth2/PowerAuthSDK.h | 8 +- proj-xcode/PowerAuth2/PowerAuthSDK.m | 190 +++++++--------- .../private/PA2GetTemporaryKeyRequest.h | 25 +++ .../private/PA2GetTemporaryKeyRequest.m | 30 +++ .../private/PA2GetTemporaryKeyResponse.h | 29 +++ .../private/PA2GetTemporaryKeyResponse.m | 37 ++++ .../private/PA2GetTemporaryKeyTask.h | 48 ++++ .../private/PA2GetTemporaryKeyTask.m | 195 +++++++++++++++++ proj-xcode/PowerAuth2/private/PA2HttpClient.m | 53 +++-- proj-xcode/PowerAuth2/private/PA2JwtObject.h | 38 ++++ proj-xcode/PowerAuth2/private/PA2JwtObject.m | 79 +++++++ .../PowerAuth2/private/PA2KeystoreService.h | 44 ++++ .../PowerAuth2/private/PA2KeystoreService.m | 206 ++++++++++++++++++ .../private/PA2ObjectSerialization.h | 32 ++- .../private/PA2ObjectSerialization.m | 68 ++++++ .../private/PA2PrivateCryptoHelper.h | 7 +- .../PowerAuth2/private/PA2RestApiEndpoint.h | 6 + .../PowerAuth2/private/PA2RestApiEndpoint.m | 20 ++ .../PowerAuth2/private/PA2RestApiObjects.h | 3 + proj-xcode/PowerAuth2/private/PA2Result.h | 6 + proj-xcode/PowerAuth2/private/PA2Result.m | 5 + .../PowerAuth2/private/PowerAuthSDK+Private.h | 4 + proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h | 5 + .../PowerAuthCore/PowerAuthCoreTypes.mm | 7 +- .../PowerAuthCoreEciesEncryptorTest.mm | 18 +- src/PowerAuth/Session.cpp | 4 +- 27 files changed, 1091 insertions(+), 142 deletions(-) create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.h create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.m create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.h create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.m create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.h create mode 100644 proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.m create mode 100644 proj-xcode/PowerAuth2/private/PA2JwtObject.h create mode 100644 proj-xcode/PowerAuth2/private/PA2JwtObject.m create mode 100644 proj-xcode/PowerAuth2/private/PA2KeystoreService.h create mode 100644 proj-xcode/PowerAuth2/private/PA2KeystoreService.m diff --git a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj index d453d5f3..3e3f7e4a 100644 --- a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj +++ b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj @@ -385,6 +385,14 @@ BF9D9C412174E3C7004FAE9C /* PA2UpgradeStartV3Response.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9D9C3F2174E3C7004FAE9C /* PA2UpgradeStartV3Response.h */; }; BF9D9C422174E3C7004FAE9C /* PA2UpgradeStartV3Response.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9D9C402174E3C7004FAE9C /* PA2UpgradeStartV3Response.m */; }; BFADA9C921B6C0CE001C6EC2 /* PowerAuthCustomHeaderRequestInterceptor.m in Sources */ = {isa = PBXBuildFile; fileRef = BFEC963D21B6990400FB5165 /* PowerAuthCustomHeaderRequestInterceptor.m */; }; + BFBD685C2C5A457C007AE16F /* PA2KeystoreService.m in Sources */ = {isa = PBXBuildFile; fileRef = BFBD685B2C5A457C007AE16F /* PA2KeystoreService.m */; }; + BFBD685D2C5A457C007AE16F /* PA2KeystoreService.h in Headers */ = {isa = PBXBuildFile; fileRef = BFBD685A2C5A457C007AE16F /* PA2KeystoreService.h */; }; + BFBD685E2C5A457C007AE16F /* PA2KeystoreService.h in Headers */ = {isa = PBXBuildFile; fileRef = BFBD685A2C5A457C007AE16F /* PA2KeystoreService.h */; }; + BFBD685F2C5A457C007AE16F /* PA2KeystoreService.m in Sources */ = {isa = PBXBuildFile; fileRef = BFBD685B2C5A457C007AE16F /* PA2KeystoreService.m */; }; + BFBD68742C5A838D007AE16F /* PA2JwtObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BFBD68732C5A838D007AE16F /* PA2JwtObject.m */; }; + BFBD68752C5A838D007AE16F /* PA2JwtObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BFBD68722C5A838D007AE16F /* PA2JwtObject.h */; }; + BFBD68762C5A838D007AE16F /* PA2JwtObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BFBD68722C5A838D007AE16F /* PA2JwtObject.h */; }; + BFBD68772C5A838D007AE16F /* PA2JwtObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BFBD68732C5A838D007AE16F /* PA2JwtObject.m */; }; BFC1925827FAF28F001455C1 /* TestHostAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1925727FAF28F001455C1 /* TestHostAppApp.swift */; }; BFC1925A27FAF28F001455C1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1925927FAF28F001455C1 /* ContentView.swift */; }; BFC1925C27FAF292001455C1 /* Assets-iOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFC1925B27FAF292001455C1 /* Assets-iOS.xcassets */; }; @@ -403,6 +411,18 @@ BFCEE09C216E280600B41201 /* PA2GetActivationStatusTask.h in Headers */ = {isa = PBXBuildFile; fileRef = BFCEE09A216E280600B41201 /* PA2GetActivationStatusTask.h */; }; BFCEE09D216E280600B41201 /* PA2GetActivationStatusTask.m in Sources */ = {isa = PBXBuildFile; fileRef = BFCEE09B216E280600B41201 /* PA2GetActivationStatusTask.m */; }; BFD386621F2B528600F74FF9 /* TestConfig in Resources */ = {isa = PBXBuildFile; fileRef = BFD386611F2B528600F74FF9 /* TestConfig */; }; + BFD3896E2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD3896D2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m */; }; + BFD3896F2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD3896C2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h */; }; + BFD389702C62640F0087F96D /* PA2GetTemporaryKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD3896D2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m */; }; + BFD389712C62640F0087F96D /* PA2GetTemporaryKeyRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD3896C2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h */; }; + BFD389742C62647C0087F96D /* PA2GetTemporaryKeyResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD389732C62647C0087F96D /* PA2GetTemporaryKeyResponse.m */; }; + BFD389752C62647C0087F96D /* PA2GetTemporaryKeyResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD389722C62647C0087F96D /* PA2GetTemporaryKeyResponse.h */; }; + BFD389762C62647C0087F96D /* PA2GetTemporaryKeyResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD389732C62647C0087F96D /* PA2GetTemporaryKeyResponse.m */; }; + BFD389772C62647C0087F96D /* PA2GetTemporaryKeyResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD389722C62647C0087F96D /* PA2GetTemporaryKeyResponse.h */; }; + BFD3897A2C626F7B0087F96D /* PA2GetTemporaryKeyTask.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD389792C626F7B0087F96D /* PA2GetTemporaryKeyTask.m */; }; + BFD3897B2C626F7B0087F96D /* PA2GetTemporaryKeyTask.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD389782C626F7B0087F96D /* PA2GetTemporaryKeyTask.h */; }; + BFD3897C2C626F7B0087F96D /* PA2GetTemporaryKeyTask.m in Sources */ = {isa = PBXBuildFile; fileRef = BFD389792C626F7B0087F96D /* PA2GetTemporaryKeyTask.m */; }; + BFD3897D2C626F7B0087F96D /* PA2GetTemporaryKeyTask.h in Headers */ = {isa = PBXBuildFile; fileRef = BFD389782C626F7B0087F96D /* PA2GetTemporaryKeyTask.h */; }; BFDA50362A9799BD0091A2E2 /* PowerAuthServerStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = BFDA50342A9799BD0091A2E2 /* PowerAuthServerStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; BFDA50372A9799BD0091A2E2 /* PowerAuthServerStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = BFDA50342A9799BD0091A2E2 /* PowerAuthServerStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; BFDA50382A9799BD0091A2E2 /* PowerAuthServerStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = BFDA50352A9799BD0091A2E2 /* PowerAuthServerStatus.m */; }; @@ -759,6 +779,10 @@ BFAF730E1EAA84CA005E7572 /* PowerAuthTestServerModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerAuthTestServerModel.h; sourceTree = ""; }; BFAF730F1EAA84CA005E7572 /* PowerAuthTestServerModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PowerAuthTestServerModel.m; sourceTree = ""; }; BFB47D5B20753640008A6A52 /* PowerAuthCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = PowerAuthCore.xcodeproj; sourceTree = ""; }; + BFBD685A2C5A457C007AE16F /* PA2KeystoreService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2KeystoreService.h; sourceTree = ""; }; + BFBD685B2C5A457C007AE16F /* PA2KeystoreService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2KeystoreService.m; sourceTree = ""; }; + BFBD68722C5A838D007AE16F /* PA2JwtObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2JwtObject.h; sourceTree = ""; }; + BFBD68732C5A838D007AE16F /* PA2JwtObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2JwtObject.m; sourceTree = ""; }; BFC1925527FAF28F001455C1 /* PowerAuth2TestsHostApp-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PowerAuth2TestsHostApp-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; }; BFC1925727FAF28F001455C1 /* TestHostAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHostAppApp.swift; sourceTree = ""; }; BFC1925927FAF28F001455C1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -776,6 +800,12 @@ BFD386611F2B528600F74FF9 /* TestConfig */ = {isa = PBXFileReference; lastKnownFileType = folder; name = TestConfig; path = PowerAuth2IntegrationTests/TestConfig; sourceTree = SOURCE_ROOT; }; BFD386651F2B6BE700F74FF9 /* PowerAuthTestServerConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerAuthTestServerConfig.h; sourceTree = ""; }; BFD386661F2B6BE700F74FF9 /* PowerAuthTestServerConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PowerAuthTestServerConfig.m; sourceTree = ""; }; + BFD3896C2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2GetTemporaryKeyRequest.h; sourceTree = ""; }; + BFD3896D2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2GetTemporaryKeyRequest.m; sourceTree = ""; }; + BFD389722C62647C0087F96D /* PA2GetTemporaryKeyResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2GetTemporaryKeyResponse.h; sourceTree = ""; }; + BFD389732C62647C0087F96D /* PA2GetTemporaryKeyResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2GetTemporaryKeyResponse.m; sourceTree = ""; }; + BFD389782C626F7B0087F96D /* PA2GetTemporaryKeyTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2GetTemporaryKeyTask.h; sourceTree = ""; }; + BFD389792C626F7B0087F96D /* PA2GetTemporaryKeyTask.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2GetTemporaryKeyTask.m; sourceTree = ""; }; BFDA50342A9799BD0091A2E2 /* PowerAuthServerStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PowerAuthServerStatus.h; sourceTree = ""; }; BFDA50352A9799BD0091A2E2 /* PowerAuthServerStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PowerAuthServerStatus.m; sourceTree = ""; }; BFDA50512A98B34E0091A2E2 /* PA2TimeSynchronizationServiceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PA2TimeSynchronizationServiceTests.m; sourceTree = ""; }; @@ -887,6 +917,8 @@ BFFB0B9C2167A541004A06E2 /* PA2CreateActivationRequestData.m */, 1B2EEF5C1D662C960039D92A /* PA2GetActivationStatusRequest.h */, 1B2EEF5D1D662C960039D92A /* PA2GetActivationStatusRequest.m */, + BFD3896C2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h */, + BFD3896D2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m */, BFC581022316C4BC004FFFF7 /* PA2ValidateSignatureRequest.h */, BFC581032316C4BC004FFFF7 /* PA2ValidateSignatureRequest.m */, BF30DB5A206CEAE900430C12 /* PA2VaultUnlockRequest.h */, @@ -916,6 +948,8 @@ BF1EC6CE223BD3BB00883236 /* PA2CreateActivationRecoveryData.m */, 1B2EEF611D662C960039D92A /* PA2GetActivationStatusResponse.h */, 1B2EEF621D662C960039D92A /* PA2GetActivationStatusResponse.m */, + BFD389722C62647C0087F96D /* PA2GetTemporaryKeyResponse.h */, + BFD389732C62647C0087F96D /* PA2GetTemporaryKeyResponse.m */, 1B2EEF651D662C960039D92A /* PA2VaultUnlockResponse.h */, 1B2EEF661D662C960039D92A /* PA2VaultUnlockResponse.m */, BF7751101FC487BC008455A6 /* PA2GetTokenResponse.h */, @@ -938,6 +972,8 @@ 1B2EEF561D662C960039D92A /* PA2Codable.h */, BF585FC3215BC12D00DE49C3 /* PA2ObjectSerialization.h */, BF585FC4215BC12D00DE49C3 /* PA2ObjectSerialization.m */, + BFBD68722C5A838D007AE16F /* PA2JwtObject.h */, + BFBD68732C5A838D007AE16F /* PA2JwtObject.m */, ); name = serialization; sourceTree = ""; @@ -1092,15 +1128,14 @@ BF4BED282A9F7F1E00A8A2D0 /* PowerAuthServerStatus+Private.h */, BF6B39EF27F4777D00BDF579 /* PowerAuthExternalPendingOperation+Private.h */, BF28C1B2298168C100E2CD8E /* PowerAuthUserInfo+Private.h */, - BF585FD2215D328B00DE49C3 /* PA2PrivateCryptoHelper.h */, - BF2007522152B44F001F3614 /* PA2PrivateEncryptorFactory.h */, - BF2007532152B44F001F3614 /* PA2PrivateEncryptorFactory.m */, BF3890372A97861100D7A18E /* PA2TimeSynchronizationService.h */, BF3890382A97861100D7A18E /* PA2TimeSynchronizationService.m */, BF3890312A9783D300D7A18E /* PA2GetSystemStatusTask.h */, BF3890322A9783D300D7A18E /* PA2GetSystemStatusTask.m */, BFCEE09A216E280600B41201 /* PA2GetActivationStatusTask.h */, BFCEE09B216E280600B41201 /* PA2GetActivationStatusTask.m */, + BFD389782C626F7B0087F96D /* PA2GetTemporaryKeyTask.h */, + BFD389792C626F7B0087F96D /* PA2GetTemporaryKeyTask.m */, BF28AA1527EB1EDE00FBFCEB /* PA2SessionDataProvider.h */, BF28AA1627EB1EDE00FBFCEB /* PA2SessionDataProvider.m */, BFC192A027FC8C3F001455C1 /* PA2SessionInterface.h */, @@ -1108,6 +1143,11 @@ BF28AA1027EA0A1900FBFCEB /* PA2DefaultSessionInterface.m */, BF28AA0927EA09F000FBFCEB /* PA2SharedSessionInterface.h */, BF28AA0A27EA09F000FBFCEB /* PA2SharedSessionInterface.m */, + BFBD685A2C5A457C007AE16F /* PA2KeystoreService.h */, + BFBD685B2C5A457C007AE16F /* PA2KeystoreService.m */, + BF585FD2215D328B00DE49C3 /* PA2PrivateCryptoHelper.h */, + BF2007522152B44F001F3614 /* PA2PrivateEncryptorFactory.h */, + BF2007532152B44F001F3614 /* PA2PrivateEncryptorFactory.m */, ); name = sdk; sourceTree = ""; @@ -1370,12 +1410,14 @@ BF5EB4E924C85FE200F9DDB2 /* PowerAuthToken.h in Headers */, BF0825952632E2BD00B34E24 /* PowerAuthActivationStatus+Private.h in Headers */, BF28AA1D27EB242400FBFCEB /* PowerAuthCoreSessionProvider.h in Headers */, + BFBD68762C5A838D007AE16F /* PA2JwtObject.h in Headers */, BF5EB4EA24C85FE200F9DDB2 /* PA2GetActivationStatusRequest.h in Headers */, BF5EB4EB24C85FE200F9DDB2 /* PA2CreateActivationResponseData.h in Headers */, BF5EB4EE24C85FE200F9DDB2 /* PA2GetActivationStatusResponse.h in Headers */, BF5EB4F024C85FE200F9DDB2 /* PA2RestApiObjects.h in Headers */, BFDA50572A98B6980091A2E2 /* PA2GetServerStatusResponse.h in Headers */, BF5EB4F124C85FE200F9DDB2 /* PA2PrivateTokenKeychainStore.h in Headers */, + BFD3897D2C626F7B0087F96D /* PA2GetTemporaryKeyTask.h in Headers */, BF5EB4F224C85FE200F9DDB2 /* PA2ObjectSerialization.h in Headers */, BF5EB4F324C85FE200F9DDB2 /* PA2Response.h in Headers */, BF4BED342AA08D2D00A8A2D0 /* PA2CompositeTask.h in Headers */, @@ -1404,6 +1446,7 @@ BF5EB50E24C85FE200F9DDB2 /* PA2PrivateHttpTokenProvider.h in Headers */, BF5EB50F24C85FE200F9DDB2 /* PA2ValidateSignatureRequest.h in Headers */, BF5EB51124C85FE200F9DDB2 /* PowerAuthKeychain.h in Headers */, + BFD389712C62640F0087F96D /* PA2GetTemporaryKeyRequest.h in Headers */, BF5EB51224C85FE200F9DDB2 /* PowerAuthConfiguration.h in Headers */, BFFECF2A26385BC9001DA7A9 /* PowerAuthActivationCode+Private.h in Headers */, BF4BED302A9F7F2400A8A2D0 /* PowerAuthServerStatus+Private.h in Headers */, @@ -1435,6 +1478,7 @@ BF5EB52724C85FE200F9DDB2 /* PA2Error+Decodable.h in Headers */, BF08255F263164F100B34E24 /* PowerAuthActivationRecoveryData.h in Headers */, BF5EB52824C85FE200F9DDB2 /* PowerAuthOperationTask.h in Headers */, + BFD389772C62647C0087F96D /* PA2GetTemporaryKeyResponse.h in Headers */, BF5EB52924C85FE200F9DDB2 /* PA2HttpRequest.h in Headers */, BF5EB52A24C85FE200F9DDB2 /* PA2PrivateRemoteTokenProvider.h in Headers */, BF5EB52B24C85FE200F9DDB2 /* PowerAuthClientConfiguration.h in Headers */, @@ -1444,6 +1488,7 @@ BF5EB52E24C85FE200F9DDB2 /* PA2GetActivationStatusTask.h in Headers */, BF1ED06F283CEB2700D6B380 /* PowerAuthKeychainAuthentication.h in Headers */, BF5EB52F24C85FE200F9DDB2 /* PA2CreateActivationResponse.h in Headers */, + BFBD685E2C5A457C007AE16F /* PA2KeystoreService.h in Headers */, BF08254B2631607C00B34E24 /* PowerAuthActivationStatus.h in Headers */, BFC192A227FC8C3F001455C1 /* PA2SessionInterface.h in Headers */, BF5EB53024C85FE200F9DDB2 /* PowerAuthRestApiErrorResponse.h in Headers */, @@ -1478,6 +1523,7 @@ BF585FCD215D239200DE49C3 /* PA2RestApiEndpoint.h in Headers */, BF3890392A97861100D7A18E /* PA2TimeSynchronizationService.h in Headers */, BF8CF4D82032EB41002A6B6E /* PowerAuthToken.h in Headers */, + BFBD685D2C5A457C007AE16F /* PA2KeystoreService.h in Headers */, BF8CF4D92032EB41002A6B6E /* PA2GetActivationStatusRequest.h in Headers */, BFFB0BA12167A9B4004A06E2 /* PA2CreateActivationResponseData.h in Headers */, BF0899E22A97408500AF29E5 /* PowerAuthTimeSynchronizationService.h in Headers */, @@ -1501,6 +1547,7 @@ BF8CF4E32032EB41002A6B6E /* PA2Codable.h in Headers */, BF4BED2F2A9F7F2300A8A2D0 /* PowerAuthServerStatus+Private.h in Headers */, BF8CF4E72032EB41002A6B6E /* PA2WCSessionPacket_TokenData.h in Headers */, + BFBD68752C5A838D007AE16F /* PA2JwtObject.h in Headers */, BF8CF4E82032EB41002A6B6E /* PA2PrivateTokenData.h in Headers */, BF8CF4E92032EB41002A6B6E /* PowerAuthClientSslNoValidationStrategy.h in Headers */, BF8CF4EA2032EB41002A6B6E /* PA2WCSessionPacket_ActivationStatus.h in Headers */, @@ -1557,8 +1604,10 @@ BF8CF5072032EB41002A6B6E /* PA2WCSessionPacket_Constants.h in Headers */, BF30DB5C206CEAE900430C12 /* PA2VaultUnlockRequest.h in Headers */, BFCEE09C216E280600B41201 /* PA2GetActivationStatusTask.h in Headers */, + BFD389752C62647C0087F96D /* PA2GetTemporaryKeyResponse.h in Headers */, BFDA50562A98B6980091A2E2 /* PA2GetServerStatusResponse.h in Headers */, BF8CF5082032EB41002A6B6E /* PA2CreateActivationResponse.h in Headers */, + BFD3897B2C626F7B0087F96D /* PA2GetTemporaryKeyTask.h in Headers */, BF28AA1127EA0A1900FBFCEB /* PA2DefaultSessionInterface.h in Headers */, BF8CF5092032EB41002A6B6E /* PowerAuthRestApiErrorResponse.h in Headers */, BF08256C2631670D00B34E24 /* PowerAuthActivationResult.h in Headers */, @@ -1566,6 +1615,7 @@ BF8CF50A2032EB41002A6B6E /* PowerAuthSDK.h in Headers */, BF8CF50B2032EB41002A6B6E /* PowerAuthRestApiError.h in Headers */, BF02103F2164C67F009745A2 /* PA2HttpClient.h in Headers */, + BFD3896F2C62640F0087F96D /* PA2GetTemporaryKeyRequest.h in Headers */, BF8CF50D2032EB41002A6B6E /* PA2PrivateMacros.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1952,6 +2002,7 @@ BF5EB4A624C85FE200F9DDB2 /* PowerAuthClientSslNoValidationStrategy.m in Sources */, BF5EB4A724C85FE200F9DDB2 /* PA2GetActivationStatusResponse.m in Sources */, BF31607827E895DD00EDA287 /* PA2SharedLock.m in Sources */, + BFBD685F2C5A457C007AE16F /* PA2KeystoreService.m in Sources */, BF5EB4A824C85FE200F9DDB2 /* PowerAuthSDK+Private.m in Sources */, BF0825AB2632E64600B34E24 /* PowerAuthActivationCode.m in Sources */, BF5EB4A924C85FE200F9DDB2 /* PowerAuthSystem.m in Sources */, @@ -1963,6 +2014,7 @@ BF5EB4AD24C85FE200F9DDB2 /* PA2EncryptedResponse.m in Sources */, BF5EB4AE24C85FE200F9DDB2 /* PowerAuthClientConfiguration.m in Sources */, BFDA50592A98B6980091A2E2 /* PA2GetServerStatusResponse.m in Sources */, + BFD3897C2C626F7B0087F96D /* PA2GetTemporaryKeyTask.m in Sources */, BF5EB4AF24C85FE200F9DDB2 /* PA2RemoveTokenRequest.m in Sources */, BF5EB4B224C85FE200F9DDB2 /* PowerAuthSDK.m in Sources */, BF5EB4B324C85FE200F9DDB2 /* PowerAuthRestApiError.m in Sources */, @@ -1970,11 +2022,13 @@ BF28C1AB29815D6900E2CD8E /* PowerAuthUserInfo.m in Sources */, BF5EB4B524C85FE200F9DDB2 /* PA2CreateActivationRequestData.m in Sources */, BF08254D2631607C00B34E24 /* PowerAuthActivationStatus.m in Sources */, + BFD389702C62640F0087F96D /* PA2GetTemporaryKeyRequest.m in Sources */, BF1ED071283CEB2700D6B380 /* PowerAuthKeychainAuthentication.m in Sources */, BF5EB4B624C85FE200F9DDB2 /* PA2Request.m in Sources */, BF5EB4B724C85FE200F9DDB2 /* PA2VaultUnlockResponse.m in Sources */, BF5EB4B824C85FE200F9DDB2 /* PA2PrivateTokenData.m in Sources */, BFF711E6265D505E00DB696A /* PowerAuthToken+WatchSupport.m in Sources */, + BFD389762C62647C0087F96D /* PA2GetTemporaryKeyResponse.m in Sources */, BF5EB4BA24C85FE200F9DDB2 /* PA2CreateActivationRequest.m in Sources */, BF5EB4BC24C85FE200F9DDB2 /* PA2ValidateSignatureRequest.m in Sources */, BF3890362A9783D300D7A18E /* PA2GetSystemStatusTask.m in Sources */, @@ -2011,6 +2065,7 @@ BF28A9FF27E9D54100FBFCEB /* PA2AppGroupContainer.m in Sources */, BF38903C2A97861100D7A18E /* PA2TimeSynchronizationService.m in Sources */, BF5EB4D924C85FE200F9DDB2 /* PowerAuthActivation.m in Sources */, + BFBD68772C5A838D007AE16F /* PA2JwtObject.m in Sources */, BF5EB4DA24C85FE200F9DDB2 /* PA2CreateActivationRecoveryData.m in Sources */, BF1ED0BA2844D3D200D6B380 /* PA2GroupedTask.m in Sources */, BF5EB4DB24C85FE200F9DDB2 /* PowerAuthAuthorizationHttpHeader.m in Sources */, @@ -2087,6 +2142,7 @@ files = ( BF28A9FE27E9D54100FBFCEB /* PA2AppGroupContainer.m in Sources */, BF8BFBE5215B8760001D6852 /* PA2HttpRequest.m in Sources */, + BFBD68742C5A838D007AE16F /* PA2JwtObject.m in Sources */, BF8CF4812032EB41002A6B6E /* PA2PrivateTokenKeychainStore.m in Sources */, BF1ED0C52846274700D6B380 /* PA2CreateTokenTask.m in Sources */, BF8CF4822032EB41002A6B6E /* PowerAuthClientSslNoValidationStrategy.m in Sources */, @@ -2100,6 +2156,7 @@ BFDA50382A9799BD0091A2E2 /* PowerAuthServerStatus.m in Sources */, BF8CF4862032EB41002A6B6E /* PowerAuthToken.m in Sources */, BF8CF4892032EB41002A6B6E /* PA2Response.m in Sources */, + BFBD685C2C5A457C007AE16F /* PA2KeystoreService.m in Sources */, BF28AA2E27ECA34900FBFCEB /* PowerAuthExternalPendingOperation.m in Sources */, BF8CF48B2032EB41002A6B6E /* PA2EncryptedResponse.m in Sources */, BF8BFBEF215BBA7B001D6852 /* PowerAuthClientConfiguration.m in Sources */, @@ -2114,6 +2171,7 @@ BF08256E2631670D00B34E24 /* PowerAuthActivationResult.m in Sources */, BF1ED070283CEB2700D6B380 /* PowerAuthKeychainAuthentication.m in Sources */, BF38903B2A97861100D7A18E /* PA2TimeSynchronizationService.m in Sources */, + BFD389742C62647C0087F96D /* PA2GetTemporaryKeyResponse.m in Sources */, BFFB0B9E2167A541004A06E2 /* PA2CreateActivationRequestData.m in Sources */, BF8CF4962032EB41002A6B6E /* PA2Request.m in Sources */, BF8CF4982032EB41002A6B6E /* PA2VaultUnlockResponse.m in Sources */, @@ -2141,6 +2199,8 @@ BF8CF4B72032EB41002A6B6E /* PowerAuthConfiguration.m in Sources */, BF2007552152B44F001F3614 /* PA2PrivateEncryptorFactory.m in Sources */, BF28AA2227EB4E8A00FBFCEB /* PA2Result.m in Sources */, + BFD3897A2C626F7B0087F96D /* PA2GetTemporaryKeyTask.m in Sources */, + BFD3896E2C62640F0087F96D /* PA2GetTemporaryKeyRequest.m in Sources */, BF8CF4B92032EB41002A6B6E /* PA2WCSessionPacket.m in Sources */, BFFB0BA22167A9B4004A06E2 /* PA2CreateActivationResponseData.m in Sources */, BF8CF4BC2032EB41002A6B6E /* PA2GetActivationStatusRequest.m in Sources */, diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.h b/proj-xcode/PowerAuth2/PowerAuthSDK.h index fc9d317b..cb873c48 100644 --- a/proj-xcode/PowerAuth2/PowerAuthSDK.h +++ b/proj-xcode/PowerAuth2/PowerAuthSDK.h @@ -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 @@ -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) eciesEncryptorForApplicationScopeWithCallback:(nonnull void(^)(PowerAuthCoreEciesEncryptor * _Nullable encryptor, NSError * _Nullable error))callback; + +- (nullable id) eciesEncryptorForActivationScopeWithCallback:(nonnull void(^)(PowerAuthCoreEciesEncryptor * _Nullable encryptor, NSError * _Nullable error))callback; @end diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.m b/proj-xcode/PowerAuth2/PowerAuthSDK.m index a1274c29..ae6c836e 100644 --- a/proj-xcode/PowerAuth2/PowerAuthSDK.m +++ b/proj-xcode/PowerAuth2/PowerAuthSDK.m @@ -26,6 +26,7 @@ #import "PA2AsyncOperation.h" #import "PA2ObjectSerialization.h" +#import "PA2KeystoreService.h" #import "PA2TimeSynchronizationService.h" #import "PA2PrivateTokenKeychainStore.h" #import "PA2PrivateHttpTokenProvider.h" @@ -61,6 +62,7 @@ @implementation PowerAuthSDK PowerAuthKeychainConfiguration * _keychainConfiguration; PowerAuthClientConfiguration * _clientConfiguration; + PA2KeystoreService * _keystoreService; PA2TimeSynchronizationService * _timeSynchronizationService; id _tokenStore; PA2HttpClient *_client; @@ -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) sessionProvider +{ + return _sessionInterface; +} + +- (id) 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, @@ -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 @@ -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) fetchEncryptedVaultUnlockKey:(PowerAuthAuthentication*)authentication reason:(PA2VaultUnlockReason)reason callback:(void(^)(NSString * encryptedEncryptionKey, NSError *error))callback @@ -540,11 +509,6 @@ - (BOOL) hasPendingProtocolUpgrade return _sessionInterface.hasPendingProtocolUpgrade; } -- (id) sessionProvider -{ - return _sessionInterface; -} - - (void) cancelAllPendingTasks { [_getActivationStatusTask cancel]; @@ -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* (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 @@ -1526,25 +1450,63 @@ - (NSData*) generateInvalidBiometricKey @implementation PowerAuthSDK (E2EE) -- (PowerAuthCoreEciesEncryptor*) eciesEncryptorForApplicationScope +- (id) 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) 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) 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 diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.h b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.h new file mode 100644 index 00000000..77408d33 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.h @@ -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 + +@property (nonatomic, strong) NSString * appKey; +@property (nonatomic, strong) NSString * activationId; +@property (nonatomic, strong) NSString * challenge; + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.m b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.m new file mode 100644 index 00000000..0973360e --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyRequest.m @@ -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 *)toDictionary +{ + if (_activationId) { + return @{ @"appKey" : _appKey, @"activationId": _activationId, @"challenge" : _challenge }; + } else { + return @{ @"appKey" : _appKey, @"challenge" : _challenge }; + } +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.h b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.h new file mode 100644 index 00000000..86f717bc --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.h @@ -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 + +@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 diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.m b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.m new file mode 100644 index 00000000..89b01a44 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyResponse.m @@ -0,0 +1,37 @@ +/* + * 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 "PA2GetTemporaryKeyResponse.h" +#import "PA2PrivateMacros.h" + +@implementation PA2GetTemporaryKeyResponse + +- (instancetype) initWithDictionary:(NSDictionary *)dictionary +{ + self = [super init]; + if (self) { + _appKey = PA2ObjectAs(dictionary[@"appKey"], NSString); + _activationId = PA2ObjectAs(dictionary[@"activationId"], NSString); + _challenge = PA2ObjectAs(dictionary[@"challenge"], NSString); + _keyId = PA2ObjectAs(dictionary[@"keyId"], NSString); + _publicKey = PA2ObjectAs(dictionary[@"publicKey"], NSString); + _expiration = [PA2ObjectAs(dictionary[@"expiration"], NSNumber) unsignedLongLongValue]; + _serverTime = [PA2ObjectAs(dictionary[@"serverTime"], NSNumber) unsignedLongLongValue]; + } + return self; +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.h b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.h new file mode 100644 index 00000000..60918eb0 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.h @@ -0,0 +1,48 @@ +/* + * 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 "PA2GroupedTask.h" + +@import PowerAuthCore; + +@class PA2HttpClient, PA2GetTemporaryKeyTask, PA2GetTemporaryKeyResponse; +@protocol PowerAuthCoreSessionProvider; + +/// The `PA2GetTemporaryKeyTaskDelegate` protocol allows class that create `PA2GetTemporaryKeyTask` object +/// monitor the task completion. +@protocol PA2GetTemporaryKeyTaskDelegate +@required +/// Called when the get activation task complete its execution. +- (void) getTemporaryKeyTask:(nonnull PA2GetTemporaryKeyTask*)task + didFinishWithResponse:(nullable PA2GetTemporaryKeyResponse*)response + error:(nullable NSError*)error; +@end + +/// The `PA2GetTemporaryKeyTask` implements grouped task that gets temporary encryption key from the server. +@interface PA2GetTemporaryKeyTask : PA2GroupedTask + +@property (nonatomic, readonly) PowerAuthCoreEciesEncryptorScope encryptorScope; +@property (nonatomic, strong, nullable, readonly) NSString * applicationKey; + +- (nonnull instancetype) initWithHttpClient:(nonnull PA2HttpClient*)httpClient + sessionProvider:(nonnull id)sessionProvider + sharedLock:(nonnull id)sharedLock + applicationKey:(nonnull NSString*)applicationKey + deviceRelatedKey:(nullable NSData*)deviceRelatedKey + encryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope + delegate:(nonnull id)delegate; + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.m b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.m new file mode 100644 index 00000000..25a36e2d --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2GetTemporaryKeyTask.m @@ -0,0 +1,195 @@ +/* + * 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 "PA2GetTemporaryKeyTask.h" +#import "PA2HttpClient.h" +#import "PA2RestApiEndpoint.h" +#import "PA2JwtObject.h" +#import "PA2GetTemporaryKeyRequest.h" +#import "PA2GetTemporaryKeyResponse.h" +#import "PA2ObjectSerialization.h" +#import "PA2PrivateMacros.h" + +@implementation PA2GetTemporaryKeyTask +{ + PA2HttpClient * _client; + id _sessionProvider; + __weak id _delegate; + NSData * _deviceRelatedKey; + BOOL _isApplicationScope; +} + +- (instancetype) initWithHttpClient:(PA2HttpClient*)httpClient + sessionProvider:(id)sessionProvider + sharedLock:(id)sharedLock + applicationKey:(NSString*)applicationKey + deviceRelatedKey:(NSData*)deviceRelatedKey + encryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope + delegate:(id)delegate +{ + BOOL isAppScope = encryptorScope == PowerAuthCoreEciesEncryptorScope_Application; + self = [super initWithSharedLock:sharedLock + taskName:isAppScope ? @"GetTempKey-App" : @"GetTempKey-Act"]; + if (self) { + _client = httpClient; + _sessionProvider = sessionProvider; + _deviceRelatedKey = deviceRelatedKey; + _encryptorScope = encryptorScope; + _delegate = delegate; + _isApplicationScope = isAppScope; + } + return self; +} + +- (void) onTaskStart +{ + [super onTaskStart]; + + PA2GetTemporaryKeyRequest * request = [[PA2GetTemporaryKeyRequest alloc] init]; + PA2JwtObject * requestJwt = [self prepareRequestJwt:request]; + if (!requestJwt) { + return; + } + PA2RestApiEndpoint * endpoint = [PA2RestApiEndpoint getTemporaryKey]; + id cancelable = [_client postObject:requestJwt to:endpoint completion:^(PowerAuthRestApiResponseStatus status, id response, NSError * error) { + PA2GetTemporaryKeyResponse * objectResponse = nil; + if (status == PowerAuthRestApiResponseStatus_OK && response) { + // Note that response is already PA2JwtObject. We're using the casting because the completion closure cannot use such type, due to a bug in objc compiler. + // If objective-c class implements more than one protocol (in this case PA2Decodable and PA2Encodable), then "magic" casting to a right closure type doesn't work, + // even if the requested protocol is implemented by the class. + objectResponse = [self processResponseJwt:(PA2JwtObject*)response error:&error]; + if (objectResponse && ![self validateResponse:objectResponse withRequest:request]) { + error = PA2MakeError(PowerAuthErrorCode_Encryption, @"JWT response doesn't match request"); + objectResponse = nil; + } + } + [self complete:objectResponse error:error]; + }]; + [self replaceCancelableOperation:cancelable]; +} + +- (void) onTaskCompleteWithResult:(id)result error:(NSError*)error +{ + [super onTaskCompleteWithResult:result error:error]; + [_delegate getTemporaryKeyTask:self didFinishWithResponse:result error:error]; +} + + + +#pragma mark - Request + +- (PA2JwtObject*) prepareRequestJwt:(PA2GetTemporaryKeyRequest*)request +{ + return [_sessionProvider readTaskWithSession:^PA2JwtObject*(PowerAuthCoreSession * session) { + NSString * activationId; + if (_isApplicationScope) { + activationId = nil; + } else { + activationId = _sessionProvider.activationIdentifier; + if (!activationId) { + [self complete:nil error:PA2MakeError(PowerAuthErrorCode_MissingActivation, nil)]; + return nil; + } + } + // Update input request object + request.appKey = _applicationKey; + request.activationId = activationId; + request.challenge = [[PowerAuthCoreCryptoUtils randomBytes:18] base64EncodedStringWithOptions:0]; + // Prepare JWT string + NSString * jwtHeader = @"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."; // {"alg":"HS256","typ":"JWT"} with dot separator + NSString * jwtPayload = [PA2ObjectSerialization serializeJwtObject:request]; + NSString * jwtSignedData = [jwtHeader stringByAppendingString:jwtPayload]; + PowerAuthCoreSignedData * dataToSign = [[PowerAuthCoreSignedData alloc] init]; + dataToSign.data = [jwtSignedData dataUsingEncoding:NSASCIIStringEncoding]; + dataToSign.signingDataKey = _isApplicationScope ? PowerAuthCoreSigningDataKey_HMAC_Application : PowerAuthCoreSigningDataKey_HMAC_Activation; + if (![session signDataWithHmacKey:dataToSign keys:[self signatureUnlockKeys]]) { + [self complete:nil error:PA2MakeError(PowerAuthErrorCode_Encryption, @"Failed to calculate JWT signature")]; + return nil; + } + NSString * jwtString = [[jwtSignedData stringByAppendingString:@"."] stringByAppendingString:[dataToSign.signature jwtEncodedString]]; + return [[PA2JwtObject alloc] initWithJwt:jwtString]; + }]; +} + +- (PowerAuthCoreSignatureUnlockKeys*) signatureUnlockKeys +{ + if (_isApplicationScope) { + return nil; + } + PowerAuthCoreSignatureUnlockKeys * keys = [[PowerAuthCoreSignatureUnlockKeys alloc] init]; + keys.possessionUnlockKey = _deviceRelatedKey; + return keys; +} + + +#pragma mark - Response + +- (PA2GetTemporaryKeyResponse*) processResponseJwt:(PA2JwtObject*)responseJwt error:(NSError**)error +{ + NSString * jwtString = responseJwt.jwt; + if (!jwtString) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Empty JWT response"); + return nil; + } + NSArray * jwtComponents = [jwtString componentsSeparatedByString:@"."]; + if (jwtComponents.count != 3) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Invalid JWT response"); + return nil; + } + NSString * jwtHeader = jwtComponents[0]; + NSString * jwtPayload = jwtComponents[1]; + NSString * jwtSignature = jwtComponents[2]; + if (jwtHeader.length == 0 || jwtPayload.length == 0 || jwtSignature.length == 0) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Invalid JWT response"); + return nil; + } + PA2JwtHeader * jwtHeaderObj = (PA2JwtHeader*)[PA2ObjectSerialization deserializeJwtObject:jwtHeader forClass:[PA2JwtHeader class] error:nil]; + if (!jwtHeaderObj) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Invalid JWT header in response"); + return nil; + } + if (![jwtHeaderObj.typ isEqualToString:@"JWT"]) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Unsupported JWT response"); + } + if (![jwtHeaderObj.alg isEqualToString:@"ES256"]) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Unsupported JWT algorithm in response"); + return nil; + } + PowerAuthCoreSignedData * signedData = [[PowerAuthCoreSignedData alloc] init]; + signedData.data = [[[jwtHeader stringByAppendingString:@"."] stringByAppendingString:jwtPayload] dataUsingEncoding:NSASCIIStringEncoding]; + signedData.signature = [[NSData alloc] initWithJwtEncodedString:jwtSignature]; + signedData.signingDataKey = _isApplicationScope ? PowerAuthCoreSigningDataKey_ECDSA_MasterServerKey : PowerAuthCoreSigningDataKey_ECDSA_PersonalizedKey; + BOOL valid = [_sessionProvider readBoolTaskWithSession:^BOOL(PowerAuthCoreSession * session) { + return [session verifyServerSignedData:signedData]; + }]; + if (!valid) { + *error = PA2MakeError(PowerAuthErrorCode_Encryption, @"Invalid signature in JWT response"); + return nil; + } + return [PA2ObjectSerialization deserializeJwtObject:jwtPayload forClass:[PA2GetTemporaryKeyResponse class] error:error]; +} + +- (BOOL) validateResponse:(PA2GetTemporaryKeyResponse*)response withRequest:(PA2GetTemporaryKeyRequest*)request +{ + BOOL match = [response.challenge isEqualToString:request.challenge]; + match = match && [response.appKey isEqualToString:request.appKey]; + if (!_isApplicationScope) { + match = match && [response.activationId isEqualToString:request.activationId]; + } + return match; +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2HttpClient.m b/proj-xcode/PowerAuth2/private/PA2HttpClient.m index 9ad4ad27..6154c7a3 100644 --- a/proj-xcode/PowerAuth2/private/PA2HttpClient.m +++ b/proj-xcode/PowerAuth2/private/PA2HttpClient.m @@ -18,6 +18,7 @@ #import "PA2AsyncOperation.h" #import "PA2CompositeTask.h" #import "PA2PrivateMacros.h" +#import "PA2KeystoreService.h" #import "PowerAuthLog.h" #import "PA2Result.h" @@ -174,9 +175,15 @@ static void _LogHttpResponse(PA2RestApiEndpoint * endpoint, NSHTTPURLResponse * completion:(void(^)(PowerAuthRestApiResponseStatus status, id response, NSError * error))completion cancel:(void(^)(void))customCancelBlock { - if (endpoint.requireSynchronizedTime && !_timeService.isTimeSynchronized) { - // Endpoint require encryption and time is not synchronized yet. We have to create a composite task that cover both - // time synchronization and actual request execution. + PA2KeystoreService * keystoreService = _cryptoHelper.keystoreService; + + PowerAuthCoreEciesEncryptorScope encryptorScope = endpoint.isEncryptedWithApplicationScope ? PowerAuthCoreEciesEncryptorScope_Application : PowerAuthCoreEciesEncryptorScope_Activation; + BOOL requireTimeSynchronization = endpoint.requireSynchronizedTime && !_timeService.isTimeSynchronized; + BOOL requireEncryptionKey = endpoint.isEncrypted && ![keystoreService hasKeyForEncryptorScope:encryptorScope]; + + if (requireTimeSynchronization || requireEncryptionKey) { + // Endpoint require encryption key or time is not synchronized yet. We have to create a composite task that handle multiple + // requests before an actual request is executed. PA2CompositeTask * compositeTask = [[PA2CompositeTask alloc] initWithCancelBlock:customCancelBlock]; // Prepare common completion block with the composite task. void (^compositeCompletion)(PowerAuthRestApiResponseStatus, id, NSError *) = ^(PowerAuthRestApiResponseStatus status, id response, NSError *error) { @@ -189,18 +196,34 @@ static void _LogHttpResponse(PA2RestApiEndpoint * endpoint, NSHTTPURLResponse * } }); }; - // Start the time synchronization - id synchronizationTask = [_timeService synchronizeTimeWithCallback:^(NSError * error) { - if (!error) { - // The time has been successfully synchronized, we can continue with the actual request. - NSOperation* actualOperation = [self postOperationWithObject:object to:endpoint auth:authentication completion:compositeCompletion cancel:nil]; - [compositeTask replaceOperationTask:actualOperation]; - } else { - // Report error to composite completion. - compositeCompletion(PowerAuthRestApiResponseStatus_ERROR, nil, error); - } - } callbackQueue:_completionQueue]; - [compositeTask replaceOperationTask:synchronizationTask]; + // Now determine what type of task should be executed before an actual task. + if (requireEncryptionKey) { + // Acquire temporary encryption key. This also synchronizes time as a side effect. + id getKeyTask = [keystoreService createKeyForEncryptorScope:encryptorScope callback:^(NSError * error) { + if (!error) { + // The temporary encryption key has been successfully obtained, we can continue with the actual request. + NSOperation* actualOperation = [self postOperationWithObject:object to:endpoint auth:authentication completion:compositeCompletion cancel:nil]; + [compositeTask replaceOperationTask:actualOperation]; + } else { + // Report error to composite completion. + compositeCompletion(PowerAuthRestApiResponseStatus_ERROR, nil, error); + } + }]; + [compositeTask replaceOperationTask:getKeyTask]; + } else { + // Start the time synchronization + id synchronizationTask = [_timeService synchronizeTimeWithCallback:^(NSError * error) { + if (!error) { + // The time has been successfully synchronized, we can continue with the actual request. + NSOperation* actualOperation = [self postOperationWithObject:object to:endpoint auth:authentication completion:compositeCompletion cancel:nil]; + [compositeTask replaceOperationTask:actualOperation]; + } else { + // Report error to composite completion. + compositeCompletion(PowerAuthRestApiResponseStatus_ERROR, nil, error); + } + } callbackQueue:_completionQueue]; + [compositeTask replaceOperationTask:synchronizationTask]; + } return compositeTask; } // Endpoint doesn't require time synchronization, or time is already synchronized. diff --git a/proj-xcode/PowerAuth2/private/PA2JwtObject.h b/proj-xcode/PowerAuth2/private/PA2JwtObject.h new file mode 100644 index 00000000..20ac6834 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2JwtObject.h @@ -0,0 +1,38 @@ +/* + * 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 PA2JwtHeader : NSObject + +- (instancetype) initJwtWithAlg:(NSString*)alg; + +- (instancetype) initWithTyp:(NSString*)typ + withAlg:(NSString*)alg; + +@property (nonatomic, strong) NSString * typ; +@property (nonatomic, strong) NSString * alg; + +@end + + +@interface PA2JwtObject : NSObject + +- (instancetype) initWithJwt:(NSString*)jwt; + +@property (nonatomic, strong) NSString * jwt; + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2JwtObject.m b/proj-xcode/PowerAuth2/private/PA2JwtObject.m new file mode 100644 index 00000000..8b6a18d3 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2JwtObject.m @@ -0,0 +1,79 @@ +/* + * 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 "PA2JwtObject.h" +#import "PA2PrivateMacros.h" + +#pragma mark - JWT Header + +@implementation PA2JwtHeader + +- (instancetype) initWithTyp:(NSString*)typ + withAlg:(NSString*)alg +{ + self = [super init]; + if (self) { + _typ = typ; + _alg = alg; + } + return self; +} + +- (instancetype) initJwtWithAlg:(NSString *)alg +{ + return [self initWithTyp:@"JWT" withAlg:alg]; +} + +- (instancetype) initWithDictionary:(NSDictionary *)dictionary +{ + return [self initWithTyp:PA2ObjectAs(dictionary[@"typ"], NSString) + withAlg:PA2ObjectAs(dictionary[@"typ"], NSString)]; +} + +- (NSDictionary *)toDictionary +{ + return @{ + @"typ": _typ, + @"alg": _alg + }; +} + +@end + +#pragma mark - JWT Object + +@implementation PA2JwtObject + +- (instancetype) initWithJwt:(NSString*)jwt +{ + self = [super init]; + if (self) { + _jwt = jwt; + } + return self; +} + +- (instancetype) initWithDictionary:(NSDictionary *)dictionary +{ + return [self initWithJwt:PA2ObjectAs(dictionary[@"jwt"], NSString)]; +} + +- (NSDictionary *) toDictionary +{ + return @{ @"jwt": _jwt }; +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2KeystoreService.h b/proj-xcode/PowerAuth2/private/PA2KeystoreService.h new file mode 100644 index 00000000..bbd9c862 --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2KeystoreService.h @@ -0,0 +1,44 @@ +/* + * 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 + +#import "PA2SessionInterface.h" +#import "PA2TimeSynchronizationService.h" +#import "PA2GetTemporaryKeyTask.h" + +/// The `PA2KeystoreService` manages temporary encryption keys for PowerAuthSDK instance. +@interface PA2KeystoreService : NSObject + +- (nonnull instancetype) initWithHttpClient:(nonnull PA2HttpClient*)httpClient + timeService:(nonnull id)timeService + deviceRelatedKey:(nonnull NSData*)deviceRelatedKey + sessionSetup:(nonnull PowerAuthCoreSessionSetup*)sessionSetup + sharedLock:(nonnull id)sharedLock; +/** + Determine whether instance of this service contains a temporary encryption key for the requested encryption scope. + */ +- (BOOL) hasKeyForEncryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope; + +/** + Create a temporary encryption key for the requested scope. If such key already exists and is still valid, then function does nothing + and returns nil. If the key is not available, or is already expired, then the function returns asynchronous task with an underlying + HTTP request. + */ +- (nullable id) createKeyForEncryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope + callback:(nonnull void(^)(NSError * _Nullable error))callback; + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2KeystoreService.m b/proj-xcode/PowerAuth2/private/PA2KeystoreService.m new file mode 100644 index 00000000..5592b45d --- /dev/null +++ b/proj-xcode/PowerAuth2/private/PA2KeystoreService.m @@ -0,0 +1,206 @@ +/* + * 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 "PA2KeystoreService.h" +#import "PA2PrivateMacros.h" +#import "PA2HttpClient.h" +#import "PA2GetTemporaryKeyResponse.h" +#import + +#pragma mark - Service data + +@interface PA2PublicKeyInfo : NSObject + +- (instancetype) initWithScope:(PowerAuthCoreEciesEncryptorScope)scope; + +@property (nonatomic, readonly) PowerAuthCoreEciesEncryptorScope scope; +@property (nonatomic, strong) PA2GetTemporaryKeyTask * task; +@property (nonatomic, assign) NSTimeInterval expiration; +@property (nonatomic, strong) id timeSynchronizationTask; + +- (void) clearTask; + +@end + +#pragma mark - Service implementation + +@implementation PA2KeystoreService +{ + id _sessionInterface; + id _timeService; + id _lock; + PA2HttpClient * _httpClient; + NSString * _applicationKey; + NSData * _deviceRelatedKey; + + PA2PublicKeyInfo * _pkiAppScope; + PA2PublicKeyInfo * _pkiActScope; +} + +- (instancetype) initWithHttpClient:(PA2HttpClient*)httpClient + timeService:(id)timeService + deviceRelatedKey:(NSData*)deviceRelatedKey + sessionSetup:(PowerAuthCoreSessionSetup*)sessionSetup + sharedLock:(id)sharedLock +{ + self = [super init]; + if (self) { + _sessionInterface = httpClient.sessionInterface; + _timeService = timeService; + _httpClient = httpClient; + _lock = sharedLock; + _applicationKey = sessionSetup.applicationKey; + _deviceRelatedKey = deviceRelatedKey; + _pkiAppScope = [[PA2PublicKeyInfo alloc] initWithScope:PowerAuthCoreEciesEncryptorScope_Application]; + _pkiActScope = [[PA2PublicKeyInfo alloc] initWithScope:PowerAuthCoreEciesEncryptorScope_Activation]; + } + return self; +} + +- (id) createKeyForEncryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope callback:(void (^)(NSError *))callback +{ + if (encryptorScope == PowerAuthCoreEciesEncryptorScope_Activation && ![self hasValidActivation]) { + callback(PA2MakeError(PowerAuthErrorCode_MissingActivation, nil)); + return nil; + } + if ([self hasKeyForEncryptorScope:encryptorScope]) { + callback(nil); + return nil; + } + + [_lock lock]; + + id task; + PA2PublicKeyInfo * pki = [self pkiForScope:encryptorScope]; + PA2GetTemporaryKeyTask * mainTask = pki.task; + if (!mainTask) { + mainTask = [[PA2GetTemporaryKeyTask alloc] initWithHttpClient:_httpClient + sessionProvider:_sessionInterface + sharedLock:_lock + applicationKey:_applicationKey + deviceRelatedKey:_deviceRelatedKey + encryptorScope:encryptorScope + delegate:self]; + pki.task = mainTask; + pki.timeSynchronizationTask = [_timeService startTimeSynchronizationTask]; + } + task = [mainTask createChildTask:^(PA2GetTemporaryKeyResponse * _Nullable result, NSError * _Nullable error) { + callback(error); + }]; + [_lock unlock]; + return task; +} + +- (PA2PublicKeyInfo*) pkiForScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope +{ + return encryptorScope == PowerAuthCoreEciesEncryptorScope_Application ? _pkiAppScope : _pkiActScope; +} + +- (BOOL) hasKeyForEncryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope +{ + // This function is using access to two separately locked sections. The goal is to do not + // overlap the critical sections. So, we have to query information in two separate steps. + BOOL keyIsExpired; + BOOL keyIsSet; + + [_lock lock]; + PA2PublicKeyInfo * pki = [self pkiForScope:encryptorScope]; + NSTimeInterval expiration = pki.expiration; + keyIsSet = expiration >= 0.0; + keyIsExpired = expiration < [_timeService currentTime]; + if (keyIsExpired) { + pki.expiration = -1; + } + [_lock unlock]; + + return [_sessionInterface readBoolTaskWithSession:^BOOL(PowerAuthCoreSession * session) { + BOOL hasKey = [session hasPublicKeyForEciesScope:encryptorScope]; + if (hasKey && keyIsExpired && keyIsSet) { + PowerAuthLog(@"Removing expired public key for ECIES encryptor %d", encryptorScope); + [session removePublicKeyForEciesScope:encryptorScope]; + hasKey = NO; + } + return hasKey; + }]; +} + +#pragma mark - PA2GetTemporaryKeyTaskDelegate + +- (void) getTemporaryKeyTask:(PA2GetTemporaryKeyTask *)task didFinishWithResponse:(PA2GetTemporaryKeyResponse *)response error:(NSError *)error +{ + // [_lock lock] is guaranteed, because this method is called from task's completion while locked with shared lock. + // So, we can freely mutate objects in this instance. + PowerAuthCoreEciesEncryptorScope scope = task.encryptorScope; + PA2PublicKeyInfo * pki = [self pkiForScope:scope]; + if (pki.task == task) { + if (response) { + NSTimeInterval receivedServerTime = 0.001 * (NSTimeInterval)response.serverTime; + [_timeService completeTimeSynchronizationTask:pki.timeSynchronizationTask withServerTime:receivedServerTime]; + [self updatePublicKeyForEncryptorScope:scope withResponse:response]; + } + [pki clearTask]; + } +} + +- (BOOL) updatePublicKeyForEncryptorScope:(PowerAuthCoreEciesEncryptorScope)encryptorScope withResponse:(PA2GetTemporaryKeyResponse*)response +{ + BOOL success = [_sessionInterface readBoolTaskWithSession:^BOOL(PowerAuthCoreSession * session) { + PowerAuthCoreErrorCode ec = [session setPublicKeyForEciesScope:encryptorScope publicKey:response.publicKey publicKeyId:response.keyId]; + if (ec != PowerAuthCoreErrorCode_Ok) { + PowerAuthLog(@"Failed to update public key for ECIES encryption. Code = %d", ec); + return NO; + } + return YES; + }]; + if (success) { + PA2PublicKeyInfo * pki = [self pkiForScope:encryptorScope]; + pki.expiration = 0.001 * response.expiration; + PowerAuthLog(@"Saving public key for ECIES encryptor %d", encryptorScope); + } + return success; +} + + +#pragma mark - Support functions + +- (BOOL) hasValidActivation +{ + return [[_sessionInterface readTaskWithSession:^id _Nullable(PowerAuthCoreSession * session) { + return @([session hasValidActivation]); + }] boolValue]; +} + +@end + + +@implementation PA2PublicKeyInfo + +- (instancetype) initWithScope:(PowerAuthCoreEciesEncryptorScope)scope +{ + self = [super init]; + if (self) { + _scope = scope; + } + return self; +} + +- (void) clearTask +{ + _task = nil; + _timeSynchronizationTask = nil; +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.h b/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.h index 3feb9144..9b8004b6 100644 --- a/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.h +++ b/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.h @@ -30,7 +30,7 @@ Serializes PA2NetworkObject into JSON data. If the object is nil, then data with an empty brackets is returned (e.g. "{}") */ -+ (NSData*) serializeObject:(id)object; ++ (NSData*) serializeObject:(NSObject*)object; /** Deserializes PA2NetworkObject from JSON Data. You must specify an object's class to @@ -83,3 +83,33 @@ error:(NSError**)error; @end + + +@interface PA2ObjectSerialization (JWT) + +/** + Serialize object into Base64Url encoded string. + */ ++ (NSString*) serializeJwtObject:(id)object; + +/** + Deserialize object from Base64Url encoded string. + */ ++ (id) deserializeJwtObject:(NSString*)data forClass:(Class)aClass error:(NSError**)error; + +@end + + +@interface NSData (JWTEncoded) + +/** + Init data with Base64Url encoded string. The "JWT Encoded" naming is used to avoid conflicts with another libraries. + */ +- (instancetype) initWithJwtEncodedString:(NSString*)jwtEncodedString; + +/** + Return bytes represented as Base64Url encoded string. + */ +- (NSString*) jwtEncodedString; + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.m b/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.m index 7a08ef70..9d13f5d5 100644 --- a/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.m +++ b/proj-xcode/PowerAuth2/private/PA2ObjectSerialization.m @@ -178,3 +178,71 @@ + (NSData*) decryptData:(NSData*)data @end +#pragma mark - Base64Url + +static NSString * ConvertToBase64Url(NSString * base64) +{ + // Remove padding and replace Base64 characters with Base64Url equivalent. + return [[[base64 stringByReplacingOccurrencesOfString:@"=" withString:@""] + stringByReplacingOccurrencesOfString:@"+" withString:@"-"] + stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; +} + +static NSString * ConvertFromBase64Url(NSString * base64Url) +{ + // At first, translate special Base64Url characters into regular Base64 characters + NSString * base64 = [[base64Url stringByReplacingOccurrencesOfString:@"_" withString:@"/"] + stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; + // Padding is optional, so append '=' if no padding is found + if (![base64 hasSuffix:@"="]) { + // Append suffix if not present + NSUInteger padCount = 4 - (base64.length & 3); + if (padCount < 4) { + base64 = [base64 stringByAppendingString:[@"===" substringToIndex:padCount]]; + } + } + return base64; +} + +@implementation NSData (JWTEncoded) + +- (instancetype) initWithJwtEncodedString:(NSString *)jwtEncodedString +{ + NSString * base64 = ConvertFromBase64Url(jwtEncodedString); + if (base64) { + return [self initWithBase64EncodedString:base64 options:0]; + } + return nil; +} + +- (NSString*) jwtEncodedString +{ + return ConvertToBase64Url([self base64EncodedStringWithOptions:0]); +} + +@end + +#pragma mark - JWT + +@implementation PA2ObjectSerialization (JWT) + ++ (NSString*) serializeJwtObject:(id)object +{ + NSString * base64 = [[self serializeObject:object] base64EncodedStringWithOptions:0]; + return ConvertToBase64Url(base64); +} + ++ (id) deserializeJwtObject:(NSString*)data forClass:(Class)aClass error:(NSError**)error +{ + NSString * base64 = ConvertFromBase64Url(data); + NSData * objectData = [[NSData alloc] initWithBase64EncodedString:base64 options:0]; + if (!objectData) { + if (error) { + *error = PA2MakeError(PowerAuthErrorCode_NetworkError, @"Failed to decode JWT object."); + } + return nil; + } + return [self deserializeObject:objectData forClass:aClass error:error]; +} + +@end diff --git a/proj-xcode/PowerAuth2/private/PA2PrivateCryptoHelper.h b/proj-xcode/PowerAuth2/private/PA2PrivateCryptoHelper.h index e18dcb15..8971c366 100644 --- a/proj-xcode/PowerAuth2/private/PA2PrivateCryptoHelper.h +++ b/proj-xcode/PowerAuth2/private/PA2PrivateCryptoHelper.h @@ -20,7 +20,7 @@ @class PowerAuthAuthorizationHttpHeader; @class PowerAuthCoreEciesEncryptor; @class PowerAuthAuthentication; - +@class PA2KeystoreService; /** The `PA2PrivateCryptoHelper` protocol provides a minimal interface for a several cryptographic tasks required internally in the SDK, but provided @@ -43,4 +43,9 @@ authentication:(PowerAuthAuthentication*)authentication error:(NSError**)error; +/** + Returns instance of keystore service. + */ +- (PA2KeystoreService*) keystoreService; + @end diff --git a/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.h b/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.h index 2e700ddb..570c0c62 100644 --- a/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.h +++ b/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.h @@ -49,6 +49,10 @@ /// Returns YES, if request requires encryption @property (nonatomic, assign, readonly) BOOL isEncrypted; +/// Returns YES, if request requires application scopeed encryption +@property (nonatomic, assign, readonly) BOOL isEncryptedWithApplicationScope; + + /// Returns YES, if request needs to be signed with PA signature @property (nonatomic, assign, readonly) BOOL isSigned; @@ -82,4 +86,6 @@ + (instancetype) getSystemStatus; ++ (instancetype) getTemporaryKey; + @end diff --git a/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.m b/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.m index f9cf315b..8e0d884c 100644 --- a/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.m +++ b/proj-xcode/PowerAuth2/private/PA2RestApiEndpoint.m @@ -159,6 +159,19 @@ + (instancetype) getSystemStatus flags:FL_ALLOWED_IN_UPGRADE]; } ++ (instancetype) getTemporaryKey +{ + // FL_ALLOWED_IN_UPGRADE is probably ignored because endoint is not signed. + // We keep it only to return semantically correct information in `isAvailableInProtocolUpgrade` + return [[PA2RestApiEndpoint alloc] initWithPath:@"/pa/v3/keystore/create" + request:[PA2JwtObject class] + response:[PA2JwtObject class] + encryptor:PA2EncryptorId_None + authUriId:nil + flags:FL_ALLOWED_IN_UPGRADE]; + +} + #pragma mark - Public getters - (BOOL) isEncrypted @@ -166,6 +179,13 @@ - (BOOL) isEncrypted return _encryptor != PA2EncryptorId_None; } +- (BOOL) isEncryptedWithApplicationScope +{ + return _encryptor == PA2EncryptorId_ActivationPayload || + _encryptor == PA2EncryptorId_ActivationRequest || + _encryptor == PA2EncryptorId_GenericApplicationScope; +} + - (BOOL) isSigned { return _authUriId != nil; diff --git a/proj-xcode/PowerAuth2/private/PA2RestApiObjects.h b/proj-xcode/PowerAuth2/private/PA2RestApiObjects.h index 17aa3971..bd6714a7 100644 --- a/proj-xcode/PowerAuth2/private/PA2RestApiObjects.h +++ b/proj-xcode/PowerAuth2/private/PA2RestApiObjects.h @@ -23,6 +23,8 @@ #import "PA2RemoveTokenRequest.h" #import "PA2ConfirmRecoveryCodeRequest.h" #import "PA2EncryptedRequest.h" +#import "PA2GetTemporaryKeyRequest.h" +#import "PA2JwtObject.h" // Response objects #import "PA2GetServerStatusResponse.h" @@ -35,4 +37,5 @@ #import "PA2EncryptedResponse.h" #import "PA2UpgradeStartV3Response.h" #import "PA2ConfirmRecoveryCodeResponse.h" +#import "PA2GetTemporaryKeyResponse.h" #import diff --git a/proj-xcode/PowerAuth2/private/PA2Result.h b/proj-xcode/PowerAuth2/private/PA2Result.h index d160619c..20e8d0dd 100644 --- a/proj-xcode/PowerAuth2/private/PA2Result.h +++ b/proj-xcode/PowerAuth2/private/PA2Result.h @@ -56,6 +56,12 @@ + (nonnull PA2Result*) failure:(nonnull ResultType)result withData:(nonnull id)data; +/** + Create result object with failure or success. If both + */ ++ (nonnull PA2Result*) success:(nullable ResultType)result + orFailure:(nullable NSError*)failure; + /** Return result and set error to provided NSError pointer in case result is failure. */ diff --git a/proj-xcode/PowerAuth2/private/PA2Result.m b/proj-xcode/PowerAuth2/private/PA2Result.m index 0afb656d..83875c13 100644 --- a/proj-xcode/PowerAuth2/private/PA2Result.m +++ b/proj-xcode/PowerAuth2/private/PA2Result.m @@ -49,6 +49,11 @@ + (id) failure:(NSError*)failure withData:(id)data return [[PA2Result alloc] initWithResult:nil error:failure data:data]; } ++ (id)success:(id)result orFailure:(NSError*)failure +{ + return [[PA2Result alloc] initWithResult:result error:failure data:nil]; +} + - (id) extractResult:(NSError**)error { if (error) { diff --git a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h index 949c4251..5beb8097 100644 --- a/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h +++ b/proj-xcode/PowerAuth2/private/PowerAuthSDK+Private.h @@ -34,6 +34,10 @@ Contains instance identifier */ @property (nonatomic, strong, readonly) NSString * privateInstanceId; +/** + Contains instnace of keystore service. + */ +@property (nonatomic, strong, readonly) PA2KeystoreService * keystoreService; /** Returns key required for unlok the possesion factor. diff --git a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h index cb3adf4d..51998aea 100644 --- a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h +++ b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h @@ -49,6 +49,11 @@ String with complete cryptographic configuration. */ @property (nonatomic, strong, readonly, nonnull) NSString * configuration; +/** + Application key extracted from the cryptographic configuration. + */ +@property (nonatomic, strong, readonly, nonnull) NSString * applicationKey; + /** Optional external encryption key. If the data object size is equal to 16 bytes, then the key is considered as valid and will be used during the cryptographic operations. diff --git a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.mm b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.mm index 78a034a5..1e2ebe8e 100644 --- a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.mm +++ b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.mm @@ -60,6 +60,12 @@ - (id) initWithConfiguration:(NSString *)configuration return _setup; } +- (NSString*) applicationKey +{ + return cc7::objc::CopyToNSString(_setup.applicationKey); +} + + - (void) setExternalEncryptionKey:(NSData *)externalEncryptionKey { _setup.externalEncryptionKey = cc7::objc::CopyFromNSData(externalEncryptionKey); @@ -68,7 +74,6 @@ - (NSData*) externalEncryptionKey { return cc7::objc::CopyToNSData(_setup.externalEncryptionKey); } - @end @implementation PowerAuthCoreHTTPRequestData diff --git a/proj-xcode/PowerAuthCoreTests/PowerAuthCoreEciesEncryptorTest.mm b/proj-xcode/PowerAuthCoreTests/PowerAuthCoreEciesEncryptorTest.mm index 497b57ee..cf80f35e 100644 --- a/proj-xcode/PowerAuthCoreTests/PowerAuthCoreEciesEncryptorTest.mm +++ b/proj-xcode/PowerAuthCoreTests/PowerAuthCoreEciesEncryptorTest.mm @@ -63,6 +63,8 @@ @implementation PowerAuthCoreEciesEncryptorTest NSString * _activationId; NSArray * _sharedInfo1Values; NSUInteger _sharedInfo1Index; + NSString * _tempKeyIdActivation; + NSString * _tempKeyIdApplication; io::getlime::powerAuth::ECIESDecryptor _decryptor; @@ -89,6 +91,8 @@ - (void) setUp @[@"/pa/recovery/confirm", @"CONFIRM_RECOVERY_CODE"] ]; _timeService = [[TestTimeService alloc] init]; + _tempKeyIdActivation = [NSUUID UUID].UUIDString; + _tempKeyIdApplication = [NSUUID UUID].UUIDString; } - (void) testGenerateEciesTestVectors @@ -101,7 +105,6 @@ - (void) testGenerateEciesTestVectors NSMutableArray * arrSharedInfo1s = [NSMutableArray array]; NSMutableArray * arrEncryptedRequests = [NSMutableArray array]; NSMutableArray * arrEncryptedResponses = [NSMutableArray array]; - td[@"configuration"] = @{ @"keyMasterPrivate": [_masterKeyPair.privateKey.privateKeyBytes base64EncodedStringWithOptions:0], @"keyMasterPublic": [_masterKeyPair.publicKey.publicKeyBytes base64EncodedStringWithOptions:0], @@ -110,7 +113,9 @@ - (void) testGenerateEciesTestVectors @"applicationKey": _appKey, @"applicationSecret": _appSecret, @"transportKey": [_transportKey base64EncodedStringWithOptions:0], - @"activationId": _activationId + @"activationId": _activationId, + @"tempKeyIdApplication": _tempKeyIdApplication, + @"tempKeyIdActivation": _tempKeyIdActivation }; td[@"testData"] = tda; for (int i = 0; i < 16; i++) { @@ -152,6 +157,7 @@ - (void) testGenerateEciesTestVectors [arrScopes addObject:@(appScope)]; NSDictionary * req = @{ + @"temporaryKeyId": appScope ? _tempKeyIdApplication : _tempKeyIdActivation, @"ephemeralPublicKey": request.keyBase64, @"encryptedData": request.bodyBase64, @"mac": request.macBase64, @@ -189,6 +195,8 @@ - (void) testGenerateEciesTestVectors TestGen(@"final PublicKey masterServerPublicKey = keyConvertor.convertBytesToPublicKey(Base64.getDecoder().decode(\"%@\"));", td[@"configuration"][@"keyMasterPublic"]); TestGen(@"final PrivateKey serverPrivateKey = keyConvertor.convertBytesToPrivateKey(ByteUtils.concat(new byte[1], Base64.getDecoder().decode(\"%@\")));", td[@"configuration"][@"keyServerPrivate"]); TestGen(@"final PublicKey serverPublicKey = keyConvertor.convertBytesToPublicKey(Base64.getDecoder().decode(\"%@\"));", td[@"configuration"][@"keyServerPublic"]); + TestGen(@"final String tempKeyIdApplication = \"%@\";", _tempKeyIdApplication); + TestGen(@"final String tempKeyIdActivation = \"%@\";", _tempKeyIdActivation); TestGen(@"final String activationId = \"%@\";", _activationId); TestGen(@"final String applicationKey = \"%@\";", _appKey); TestGen(@"final String applicationSecret = \"%@\";", _appSecret); @@ -227,6 +235,7 @@ - (void) testGenerateEciesTestVectors TestGen(@"final EncryptedRequest[] encryptedRequest = {"); [arrEncryptedRequests enumerateObjectsUsingBlock:^(NSDictionary * obj, NSUInteger idx, BOOL * _Nonnull stop) { TestGen(@" new EncryptedRequest("); + TestGen(@" \"%@\",", obj[@"temporaryKeyId"]); TestGen(@" \"%@\",", obj[@"ephemeralPublicKey"]); TestGen(@" \"%@\",", obj[@"encryptedData"]); TestGen(@" \"%@\",", obj[@"mac"]); @@ -279,6 +288,7 @@ - (NSData*) sh2ForScope:(BOOL)appScope - (PowerAuthCoreEciesEncryptor*) createEncryptor:(BOOL)appScope sh1:(NSString*)sh1 { NSData * publicKeyBytes = appScope ? _masterKeyPair.publicKey.publicKeyBytes : _serverKeyPair.publicKey.publicKeyBytes; + NSString * tempKeyId = appScope ? _tempKeyIdApplication : _tempKeyIdActivation; NSData * sharedInfo1 = [sh1 dataUsingEncoding:NSUTF8StringEncoding]; NSData * sharedInfo2 = [self sh2ForScope:appScope]; NSString * activationId = appScope ? nil : _activationId; @@ -286,7 +296,9 @@ - (PowerAuthCoreEciesEncryptor*) createEncryptor:(BOOL)appScope sh1:(NSString*)s publicKey:publicKeyBytes sharedInfo1:sharedInfo1 sharedInfo2:sharedInfo2]; - encryptor.associatedMetaData = [[PowerAuthCoreEciesMetaData alloc] initWithApplicationKey:_appKey activationIdentifier:activationId]; + encryptor.associatedMetaData = [[PowerAuthCoreEciesMetaData alloc] initWithApplicationKey:_appKey + temporaryKeyId:tempKeyId + activationIdentifier:activationId]; return encryptor; } diff --git a/src/PowerAuth/Session.cpp b/src/PowerAuth/Session.cpp index 4e23bb99..47daeaa2 100644 --- a/src/PowerAuth/Session.cpp +++ b/src/PowerAuth/Session.cpp @@ -735,7 +735,7 @@ namespace powerAuth } cc7::ByteArray signing_key; if (app_scope) { - signing_key.readFromBase64String(_setup.applicationSecret); + signing_key = cc7::MakeRange(_setup.applicationSecret); } else { // Unlock transport key protocol::SignatureKeys plain; @@ -744,7 +744,7 @@ namespace powerAuth CC7_LOG("Session %p: HmacSign: You have to provide possession key.", this); return EC_WrongParam; } - signing_key = plain.transportKey; + signing_key = crypto::HMAC_SHA256(cc7::MakeRange(_setup.applicationSecret), plain.transportKey); } data.signature = crypto::HMAC_SHA256(data.data, signing_key); return data.signature.empty() ? EC_Encryption : EC_Ok;