diff --git a/include/PowerAuth/PublicTypes.h b/include/PowerAuth/PublicTypes.h index f9d5d6d2..6792861a 100644 --- a/include/PowerAuth/PublicTypes.h +++ b/include/PowerAuth/PublicTypes.h @@ -375,7 +375,15 @@ namespace powerAuth /** `KEY_SERVER_PRIVATE` key was used for signature calculation */ - ECDSA_PersonalizedKey = 1 + ECDSA_PersonalizedKey = 1, + /** + `APP_SECRET` key is used for HMAC-SHA256 signature calculation. + */ + HMAC_Application = 2, + /** + `KEY_TRANSPORT` key is used for HMAC-SHA256 signature calculation. + */ + HMAC_Activation = 3 }; /** @@ -398,6 +406,20 @@ namespace powerAuth signingKey(signingKey) { } + + /** + Determine whether the signing key is set to one from ECDSA variants. + */ + bool isEcdsaSignature() const { + return signingKey == ECDSA_PersonalizedKey || signingKey == ECDSA_MasterServerKey; + } + + /** + Determine whether the signing key is set to one from HMAC viarants. + */ + bool isHmacSignature() const { + return signingKey == HMAC_Activation || signingKey == HMAC_Application; + } }; diff --git a/include/PowerAuth/Session.h b/include/PowerAuth/Session.h index 2621f596..e4c39271 100644 --- a/include/PowerAuth/Session.h +++ b/include/PowerAuth/Session.h @@ -307,6 +307,20 @@ namespace powerAuth EC_WrongParam if data structure doesn't contain signature */ ErrorCode verifyServerSignedData(const SignedData & data) const; + + /** + Calculates HMAC-SHA256 signature with using key specified in |data|. The output signature is + also stored to provided data object. If `HMAC_Activation` key is requested, then |keys| must + contain possession factor unlock key and the session must have valid activation. + + Returns EC_Ok, if operation succeeded and signature is computed. + EC_Encryption if cryptographic operation failed. + EC_WrongState if session contains invalid setup, or valid activation is required + for the requested key. + EC_WrongParam if keys structure doesn't contain possession factor unlock key + and the key is required. + */ + ErrorCode signDataWithHmacKey(SignedData & data, const SignatureUnlockKeys & keys) const; // MARK: - Signature keys management - diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/Session.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/Session.java index 1c34b46d..d222ea37 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/Session.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/Session.java @@ -356,6 +356,17 @@ public byte[] prepareKeyValueDictionaryForDataSigning(Map keyVal @ErrorCode public native int verifyServerSignedData(SignedData signedData); + /** + * Calculates HMAC-SHA256 signature with using key specified in {@link SignedData} object. The output signature is + * also stored to provided data object. If {@link SigningDataKey#HMAC_ACTIVATION} key is requested, then unlock keys + * must object must contain possession factor unlock key and the session must have valid activation. + * @param dataToSign Object containing data to sign and the key to use for the signature calculation. + * @param unlockKeys Required for {@link SigningDataKey#HMAC_ACTIVATION} key. + * @return integer comparable to constants available at {@link ErrorCode} class. + */ + @ErrorCode + public native int signDataWithHmacKey(SignedData dataToSign, SignatureUnlockKeys unlockKeys); + // // Signature keys management // diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SignedData.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SignedData.java index 944b326b..4468f104 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SignedData.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SignedData.java @@ -30,20 +30,19 @@ public class SignedData { */ public final byte[] signature; /** - * If true, then the master server's public key is used for validation, otherwise - * the personalized server's public key is used. + * Key to use for signature validation or computation. */ - public final boolean useMasterKey; + @SigningDataKey + public final int signingKey; /** * @param data data protected with signature * @param signature signature calculated for data - * @param useMasterKey If true, then the master server's public key is used for validation, otherwise - * the personalized server's public key is used. + * @param signingKey Key used to sign data, or will be used for the signature calculation. */ - public SignedData(byte[] data, byte[] signature, boolean useMasterKey) { + public SignedData(byte[] data, byte[] signature, @SigningDataKey int signingKey) { this.data = data; this.signature = signature; - this.useMasterKey = useMasterKey; + this.signingKey = signingKey; } } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SigningDataKey.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SigningDataKey.java new file mode 100644 index 00000000..ed989694 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/core/SigningDataKey.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +package io.getlime.security.powerauth.core; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import static io.getlime.security.powerauth.core.SigningDataKey.ECDSA_MASTER_SERVER_KEY; +import static io.getlime.security.powerauth.core.SigningDataKey.ECDSA_PERSONALIZED_KEY; +import static io.getlime.security.powerauth.core.SigningDataKey.HMAC_APPLICATION; +import static io.getlime.security.powerauth.core.SigningDataKey.HMAC_ACTIVATION; + +/** + * The EciesEncryptorScope defines how {@link EciesEncryptor} encryptor + * is configured in {@link Session#getEciesEncryptor(int, SignatureUnlockKeys, byte[]) Session.getEciesEncryptor} method. + */ +@Retention(RetentionPolicy.SOURCE) +@IntDef({ECDSA_MASTER_SERVER_KEY, ECDSA_PERSONALIZED_KEY, HMAC_APPLICATION, HMAC_ACTIVATION}) +public @interface SigningDataKey { + /** + * {@code KEY_SERVER_MASTER_PRIVATE} key was used for signature calculation. + */ + int ECDSA_MASTER_SERVER_KEY = 0; + /** + * {@code KEY_SERVER_PRIVATE} key was used for signature calculation. + */ + int ECDSA_PERSONALIZED_KEY = 1; + /** + * {@code APP_SECRET} key is used for HMAC-SHA256 signature calculation. + */ + int HMAC_APPLICATION = 2; + /** + * {@code KEY_TRANSPORT} key is used for HMAC-SHA256 signature calculation. + */ + int HMAC_ACTIVATION = 3; +} diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java index ead85a59..546127e5 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java @@ -1789,7 +1789,8 @@ public boolean verifyServerSignedData(byte[] data, byte[] signature, boolean use checkForValidSetup(); // Verify signature - SignedData signedData = new SignedData(data, signature, useMasterKey); + final int signingKey = useMasterKey ? SigningDataKey.ECDSA_MASTER_SERVER_KEY : SigningDataKey.ECDSA_PERSONALIZED_KEY; + final SignedData signedData = new SignedData(data, signature, signingKey); return mSession.verifyServerSignedData(signedData) == ErrorCode.OK; } diff --git a/proj-xcode/PowerAuthCore/PowerAuthCoreSession.h b/proj-xcode/PowerAuthCore/PowerAuthCoreSession.h index 5cbc2ecf..faedbbce 100644 --- a/proj-xcode/PowerAuthCore/PowerAuthCoreSession.h +++ b/proj-xcode/PowerAuthCore/PowerAuthCoreSession.h @@ -324,6 +324,23 @@ */ - (BOOL) verifyServerSignedData:(nonnull PowerAuthCoreSignedData*)signedData; +/** + Calculates HMAC-SHA256 signature with using key specified in |dataToSign|. The output signature is + also stored to provided data object. If `HMAC_Activation` key is requested, then |unlockKeys| must + contain possession factor unlock key and the session must have valid activation. + + Returns YES if signature is calculated. In case of error, you can determine the failure reason + from DEBUG log: + PowerAuthCoreErrorCode_Ok if operation succeeded and signature is computed. + PowerAuthCoreErrorCode_Encryption if cryptographic operation failed. + PowerAuthCoreErrorCode_WrongState if session contains invalid setup, or valid activation is required + for the requested key. + PowerAuthCoreErrorCode_WrongParam if keys structure doesn't contain possession factor unlock key + and the key is required. + */ +- (BOOL) signDataWithHmacKey:(nonnull PowerAuthCoreSignedData*)dataToSign + keys:(nullable PowerAuthCoreSignatureUnlockKeys*)unlockKeys; + #pragma mark - Signature keys management /** diff --git a/proj-xcode/PowerAuthCore/PowerAuthCoreSession.mm b/proj-xcode/PowerAuthCore/PowerAuthCoreSession.mm index 38d6edc9..f7435eee 100644 --- a/proj-xcode/PowerAuthCore/PowerAuthCoreSession.mm +++ b/proj-xcode/PowerAuthCore/PowerAuthCoreSession.mm @@ -295,6 +295,22 @@ - (BOOL) verifyServerSignedData:(nonnull PowerAuthCoreSignedData*)signedData return error == EC_Ok; } +- (BOOL) signDataWithHmacKey:(nonnull PowerAuthCoreSignedData*)dataToSign + keys:(nullable PowerAuthCoreSignatureUnlockKeys*)unlockKeys +{ + REQUIRE_READ_ACCESS(); + ErrorCode error; + if (dataToSign != nil) { + SignatureUnlockKeys cpp_keys; + PowerAuthCoreSignatureUnlockKeysToStruct(unlockKeys, cpp_keys); + error = _session->signDataWithHmacKey(dataToSign.signedDataRef, cpp_keys); + } else { + error = EC_WrongParam; + } + REPORT_ERROR_CODE(@"signDataWithHmacKey", error); + return error == EC_Ok; +} + #pragma mark - Signature keys management diff --git a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h index 5be16f36..cb3adf4d 100644 --- a/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h +++ b/proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h @@ -303,6 +303,14 @@ typedef NS_ENUM(int, PowerAuthCoreSigningDataKey) { `KEY_SERVER_PRIVATE` key was used for signature calculation */ PowerAuthCoreSigningDataKey_ECDSA_PersonalizedKey = 1, + /** + `APP_SECRET` key is used for HMAC-SHA256 signature calculation. + */ + PowerAuthCoreSigningDataKey_HMAC_Application = 2, + /** + `KEY_TRANSPORT` key is used for HMAC-SHA256 signature calculation. + */ + PowerAuthCoreSigningDataKey_HMAC_Activation = 3 }; /** diff --git a/src/PowerAuth/Session.cpp b/src/PowerAuth/Session.cpp index bb2a49e1..4e23bb99 100644 --- a/src/PowerAuth/Session.cpp +++ b/src/PowerAuth/Session.cpp @@ -680,6 +680,10 @@ namespace powerAuth CC7_LOG("Session %p: ServerSig: Session has no valid setup.", this); return EC_WrongState; } + if (!data.isEcdsaSignature()) { + CC7_LOG("Session %p: ServerSig: Unsupported key.", this); + return EC_WrongParam; + } bool use_master_server_key = data.signingKey == SignedData::ECDSA_MasterServerKey; if (!use_master_server_key && !hasValidActivation()) { CC7_LOG("Session %p: ServerSig: There's no valid activation.", this); @@ -712,6 +716,39 @@ namespace powerAuth return success ? EC_Ok : EC_Encryption; } + + ErrorCode Session::signDataWithHmacKey(SignedData &data, const SignatureUnlockKeys & keys) const + { + LOCK_GUARD(); + if (!hasValidSetup()) { + CC7_LOG("Session %p: HmacSign: Session has no valid setup.", this); + return EC_WrongState; + } + if (!data.isHmacSignature()) { + CC7_LOG("Session %p: HmacSign: Unsupported key.", this); + return EC_WrongParam; + } + bool app_scope = data.signingKey == SignedData::HMAC_Application; + if (!app_scope && !hasValidActivation()) { + CC7_LOG("Session %p: ServerSig: There's no valid activation.", this); + return EC_WrongState; + } + cc7::ByteArray signing_key; + if (app_scope) { + signing_key.readFromBase64String(_setup.applicationSecret); + } else { + // Unlock transport key + protocol::SignatureKeys plain; + protocol::SignatureUnlockKeysReq unlock_request(protocol::SF_Transport, &keys, eek(), nullptr, 0); + if (false == protocol::UnlockSignatureKeys(plain, _pd->sk, unlock_request)) { + CC7_LOG("Session %p: HmacSign: You have to provide possession key.", this); + return EC_WrongParam; + } + signing_key = plain.transportKey; + } + data.signature = crypto::HMAC_SHA256(data.data, signing_key); + return data.signature.empty() ? EC_Encryption : EC_Ok; + } // MARK: - Signature keys management -