Skip to content

Commit

Permalink
Core: #604: Support for JWT-HMAC signature calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
hvge committed Aug 1, 2024
1 parent af3938f commit ce3313b
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 9 deletions.
24 changes: 23 additions & 1 deletion include/PowerAuth/PublicTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/**
Expand All @@ -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;
}
};


Expand Down
14 changes: 14 additions & 0 deletions include/PowerAuth/Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,17 @@ public byte[] prepareKeyValueDictionaryForDataSigning(Map<String, String> 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
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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 <code>EciesEncryptorScope</code> 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
17 changes: 17 additions & 0 deletions proj-xcode/PowerAuthCore/PowerAuthCoreSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
16 changes: 16 additions & 0 deletions proj-xcode/PowerAuthCore/PowerAuthCoreSession.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions proj-xcode/PowerAuthCore/PowerAuthCoreTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/**
Expand Down
37 changes: 37 additions & 0 deletions src/PowerAuth/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 -

Expand Down

0 comments on commit ce3313b

Please sign in to comment.