Skip to content

Commit

Permalink
Core: #604: Support for ECIES with temporary keys
Browse files Browse the repository at this point in the history
- Increased protocol version to 3.3
  • Loading branch information
hvge committed Jul 30, 2024
1 parent 9d1d9ec commit af3938f
Show file tree
Hide file tree
Showing 30 changed files with 602 additions and 121 deletions.
2 changes: 1 addition & 1 deletion include/PowerAuth/ECIES.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ namespace powerAuth
public:
/// Build associated data from provided parameters. If activationId is not available (e.g. for ECIES in application scope)
/// then you can provide an empty string.
static cc7::ByteArray buildAssociatedData(const std::string & applicationKey, const std::string & activationId);
static cc7::ByteArray buildAssociatedData(const std::string & applicationKey, const std::string & temporaryKeyId, const std::string & activationId);
};


Expand Down
2 changes: 1 addition & 1 deletion include/PowerAuth/PublicTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ namespace powerAuth
};

/**
Returns textual representation for given protocol version. For example, for `Version_V3` returns "3.2".
Returns textual representation for given protocol version. For example, for `Version_V3` returns "3.3".
You can use `Version_NA` to get the lastest supported version.
*/
extern std::string Version_GetMaxSupportedHttpProtocolVersion(Version protocol_version);
Expand Down
44 changes: 41 additions & 3 deletions include/PowerAuth/Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace powerAuth
{
struct PersistentData;
struct ActivationData;
struct SessionData;
}

/**
Expand Down Expand Up @@ -86,9 +87,10 @@ namespace powerAuth

/**
Resets session into its initial state. The existing session's setup and the external encryption
key is preserved by this call.
key is preserved by this call. If |full_reset\ parameter is true, then also resets data not relevant
to the activation state. For example, ECIES public key for application scope.
*/
void resetSession();
void resetSession(bool full_reset = true);


// MARK: - State probing -
Expand Down Expand Up @@ -493,7 +495,37 @@ namespace powerAuth
*/
ErrorCode getEciesEncryptor(ECIESEncryptorScope scope, const SignatureUnlockKeys & keys,
const cc7::ByteRange & sharedInfo1, ECIESEncryptor & out_encryptor) const;

/**
Sets a server's public key and its identifier for ECIES encryption. The scope of the encryption is
determined by |scope| parameter.
Returns EC_Ok if operation succeeded.
EC_WrongState if activation scope is used and the session has no valid activation, or
if session object has no valid setup.
EC_WrongParam if public key is empty, or doesn't contain Base64 encoded data, or
if key identifier is empty.
*/
ErrorCode setPublicKeyForEciesScope(ECIESEncryptorScope scope, const std::string & public_key, const std::string & key_id);

/**
Removes a server's public key and its identifier store for the given scope. It's safe to call this
function if key for given scope is not set.
*/
void removePublicKeyForEciesScope(ECIESEncryptorScope scope);

/**
Determines whether session contains stored server's public key for ECIES scope.
*/
bool hasPublicKeyForEciesScope(ECIESEncryptorScope scope) const;

/**
Returns identifier of server's public key for given scope. If no key is set, or if function is called in
wrong state, then returns empty string.
*/
std::string getPublicKeyIdForEciesScope(ECIESEncryptorScope scope) const;

public:

// MARK: - Utilities for generic keys -

/**
Expand Down Expand Up @@ -664,6 +696,12 @@ namespace powerAuth
*/
protocol::ActivationData * _ad;

/**
Pointer to private session data structure. The pointer is valid during the whole
Session object lifetime.
*/
protocol::SessionData * _sd;

/**
Commits a |new_pd| and |new_state| as a new valid session state.
Check documentation in method's implementation for details.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public enum ProtocolVersion {
V2_1(21, "2.1"),
V3(30, "3.0"),
V3_1(31, "3.1"),
V3_2(32, "3.2");
V3_2(32, "3.2"),
V3_3(33, "3.3");

public final int version;
public final String versionForHeader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public enum ServerVersion {
V1_6_0("1.6", 1006000, ProtocolVersion.V3_2),
V1_7_0("1.7", 1007000, ProtocolVersion.V3_2),
V1_8_0("1.8", 1008000, ProtocolVersion.V3_2),
V1_9_0("1.9", 1009000, ProtocolVersion.V3_3),

;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ public SessionSetup getSessionSetup() {
/**
* Resets session into its initial state. The existing session's setup and EEK is preserved
* after the call.
*
* @param fullReset If {@code true}, then also resets data not relevant to the activation state. For example, ECIES public
* key for application scope.
*/
public native void resetSession();
public native void resetSession(boolean fullReset);

/**
* Returns true if dynamic library was compiled with a debug features. It is highly recommended
Expand Down Expand Up @@ -535,7 +538,7 @@ public byte[] prepareKeyValueDictionaryForDataSigning(Map<String, String> keyVal
*
* @return {@link EciesEncryptor} object or nil in case of error
*/
public EciesEncryptor getEciesEncryptor(int scope, SignatureUnlockKeys unlockKeys, byte[] sharedInfo1) {
public EciesEncryptor getEciesEncryptor(@EciesEncryptorScope int scope, SignatureUnlockKeys unlockKeys, byte[] sharedInfo1) {
return getEciesEncryptorImpl(scope, unlockKeys, sharedInfo1, timeService);
}

Expand All @@ -551,7 +554,44 @@ public EciesEncryptor getEciesEncryptor(int scope, SignatureUnlockKeys unlockKey
*
* @return {@link EciesEncryptor} object or nil in case of error
*/
private native EciesEncryptor getEciesEncryptorImpl(int scope, SignatureUnlockKeys unlockKeys, byte[] sharedInfo1, ICoreTimeService timeService);
private native EciesEncryptor getEciesEncryptorImpl(@EciesEncryptorScope int scope, SignatureUnlockKeys unlockKeys, byte[] sharedInfo1, ICoreTimeService timeService);

/**
* Sets a server's public key and its identifier for ECIES encryption.
* @param scope Scope of the server's public key.
* @param publicKey Public key encoded as Base64 string.
* @param publicKeyId Identifier of new public key.
* @return <ul>
* <li>{@link ErrorCode#OK} in case of success.</li>
* <li>{@link ErrorCode#WrongParam} if null or empty string is provided.</li>
* <li>{@link ErrorCode#WrongState} if activation scope is used and the session has no valid activation.</li>
* </ul>
*/
@ErrorCode
public native int setPublicKeyForEciesScope(@EciesEncryptorScope int scope, @NonNull String publicKey, @NonNull String publicKeyId);

/**
* Removes a server's public key and its identifier store for the given scope. It's safe to call this
* function if key for given scope is not set.
* @param scope Scope of the key to remove.
*/
public native void removePublicKeyForEciesScope(@EciesEncryptorScope int scope);

/**
* Determines whether session contains stored server's public key for ECIES scope.
* @param scope Scope of the key.
* @return {@code true} if session has stored server's public key for the requested scope.
*/
public native boolean hasPublicKeyForEciesScope(@EciesEncryptorScope int scope);

/**
* Returns identifier of server's public key for given scope.
* @param scope Scope of the key.
* @return Identifier of the stored server's public key, or {@code null} if no key is stored, or method is called
* in the wrong activation state.
*/
@Nullable
public native String getPublicKeyIdForEciesScope(@EciesEncryptorScope int scope);

//
// Utilities
Expand Down Expand Up @@ -656,7 +696,7 @@ public EciesEncryptor getEciesEncryptor(int scope, SignatureUnlockKeys unlockKey

/**
* Return textual representation for given protocol version. For example, for {@link ProtocolVersion#V3}
* returns {@code "3.2"}. You can use {@link ProtocolVersion#NA} to get the latest supported version.
* returns {@code "3.3"}. You can use {@link ProtocolVersion#NA} to get the latest supported version.
*
* @param version Version to convert to string
* @return Textual representation for given protocol version.
Expand All @@ -668,7 +708,7 @@ public static String getMaxSupportedHttpProtocolVersion(ProtocolVersion version)

/**
* Return textual representation for given integer value of protocol version. For example,
* for {@link ProtocolVersion#V3} returns {@code "3.2"}. You can use {@link ProtocolVersion#NA}
* for {@link ProtocolVersion#V3} returns {@code "3.3"}. You can use {@link ProtocolVersion#NA}
* to get the latest supported version.
*
* @param protocolVersionValue Integer value from {@link ProtocolVersion} enum.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public EciesEncryptorFactory(@NonNull Session session, @Nullable byte[] possessi
final byte[] sharedInfo1Bytes = sharedInfo1 != null ? sharedInfo1.getBytes(Charset.defaultCharset()) : null;
final SignatureUnlockKeys unlockKeys;
final String activationId;
final String temporaryKeyId = mSession.getPublicKeyIdForEciesScope(scope);
if (temporaryKeyId == null) {
throw new PowerAuthErrorException(PowerAuthErrorCodes.ENCRYPTION_ERROR, "Temporary key for ECIES is not set");
}
if (scope == EciesEncryptorScope.ACTIVATION) {
if (mPossessionUnlockKey == null) {
throw new PowerAuthErrorException(PowerAuthErrorCodes.WRONG_PARAMETER, "Device related key is missing for activation scoped encryptor");
Expand All @@ -97,7 +101,7 @@ public EciesEncryptorFactory(@NonNull Session session, @Nullable byte[] possessi
if (encryptor == null) {
throw new PowerAuthErrorException(PowerAuthErrorCodes.ENCRYPTION_ERROR, "Failed to create ECIES encryptor");
}
encryptor.setMetadata(new EciesMetadata(mSession.getApplicationKey(), activationId));
encryptor.setMetadata(new EciesMetadata(mSession.getApplicationKey(), temporaryKeyId, activationId));
return encryptor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
public class EciesMetadata {

private final @NonNull String applicationKey;
private final @NonNull String temporaryKeyId;
private final @Nullable String activationIdentifier;

/**
* @param applicationKey Base64 string with an application key cryptographic constant
* @param activationIdentifier String with an activation identifier
*/
public EciesMetadata(@NonNull String applicationKey, @Nullable String activationIdentifier) {
public EciesMetadata(@NonNull String applicationKey, @NonNull String temporaryKeyId, @Nullable String activationIdentifier) {
this.applicationKey = applicationKey;
this.temporaryKeyId = temporaryKeyId;
this.activationIdentifier = activationIdentifier;
}

Expand All @@ -47,6 +49,13 @@ public EciesMetadata(@NonNull String applicationKey, @Nullable String activation
return applicationKey;
}

/**
* @return Temporary key identifier
*/
public @NonNull String getApplicationKey() {
return applicationKey;
}

/**
* @return Base64 String with an activation identifier
*/
Expand All @@ -67,7 +76,8 @@ public EciesMetadata(@NonNull String applicationKey, @Nullable String activation
* @return String with HTTP request header's value.
*/
public @NonNull String getHttpHeaderValue() {
final String result = "PowerAuth version=\"3.2\" application_key=\"" + applicationKey + "\"";
final String result = "PowerAuth version=\"3.3\" application_key=\"" + applicationKey +
"\" temporary_key_id=\"" + temporaryKeyId + "\"";
if (activationIdentifier != null) {
return result + " activation_id=\"" + activationIdentifier + "\"";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ public void saveSerializedState() {
*/
@CheckResult
public boolean restoreState(byte[] state) {
mSession.resetSession();
mSession.resetSession(false);
final int result = mSession.deserializeState(state);
return result == ErrorCode.OK;
}
Expand Down Expand Up @@ -730,27 +730,27 @@ public void onNetworkResponse(@NonNull ActivationLayer1Response response) {

} catch (PowerAuthErrorException e) {
// In case of error, reset the session & report that exception
mSession.resetSession();
mSession.resetSession(false);
listener.onActivationCreateFailed(e);
}
}

@Override
public void onNetworkError(@NonNull Throwable throwable) {
// In case of error, reset the session & report that exception
mSession.resetSession();
mSession.resetSession(false);
listener.onActivationCreateFailed(throwable);
}

@Override
public void onCancel() {
// In case of cancel, reset the session
mSession.resetSession();
mSession.resetSession(false);
}
});

} catch (final PowerAuthErrorException e) {
mSession.resetSession();
mSession.resetSession(false);
dispatchCallback(new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -1628,7 +1628,7 @@ public void removeActivationLocal(@NonNull Context context, boolean removeShared
getTokenStore().removeAllLocalTokens(context);

// Reset C++ session
mSession.resetSession();
mSession.resetSession(false);
// Serialize will notify state listener
saveSerializedState();
// Cancel possible pending activation status task
Expand Down
8 changes: 4 additions & 4 deletions proj-xcode/PowerAuth2/PowerAuthSDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ - (void) cancelAllPendingTasks
decryptor:decryptor
session:session];
}
[session resetSession];
[session resetSession:NO];
return [PA2Result failure:error ? error : PA2MakeError(PowerAuthErrorCode_InvalidActivationData, nil)];
}] extractResult:&error];

Expand Down Expand Up @@ -837,7 +837,7 @@ - (NSString*) activationFingerprint
localError = PA2MakeError(PowerAuthErrorCode_InvalidActivationState, nil);
}
if (resetState) {
[session resetSession];
[session resetSession:NO];
}
return [PA2Result failure:localError];
}
Expand Down Expand Up @@ -884,7 +884,7 @@ - (NSString*) activationFingerprint
}
}
// If failure, then reset session and report error.
[session resetSession];
[session resetSession:NO];
return [PA2Result failure:localError];
}

Expand Down Expand Up @@ -987,7 +987,7 @@ - (void) removeActivationLocal
PowerAuthLog(@"Removing activaton data from keychain failed. We can't recover from this error.");
}
[_tokenStore removeAllLocalTokens];
[session resetSession];
[session resetSession:NO];
}];
}

Expand Down
2 changes: 1 addition & 1 deletion proj-xcode/PowerAuth2/PowerAuthToken.m
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ - (PowerAuthAuthorizationHttpHeader*) generateHeader
tokenIdentifier = _tokenData.identifier;

// Prepare data for HMAC
NSString * protocolVersion = @"3.2";
NSString * protocolVersion = @"3.3";
NSNumber * currentTimeMs = @((int64_t)([timeService currentTime] * 1000.0));
NSString * currentTimeString = [currentTimeMs stringValue];
NSData * currentTimeData = [currentTimeString dataUsingEncoding:NSASCIIStringEncoding];
Expand Down
4 changes: 2 additions & 2 deletions proj-xcode/PowerAuth2/private/PA2DefaultSessionInterface.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ - (void) loadState
if (statusData) {
[_session deserializeState:statusData];
} else {
[_session resetSession];
[_session resetSession:NO];
}
_stateBefore = [_session serializedState];

Expand Down Expand Up @@ -159,7 +159,7 @@ - (void) writeVoidTaskWithSession:(void (NS_NOESCAPE ^)(PowerAuthCoreSession *))
- (void) resetSession
{
WRITE_ACCESS_LOCK();
[_session resetSession];
[_session resetSession:NO];
WRITE_ACCESS_UNLOCK();
}

Expand Down
7 changes: 6 additions & 1 deletion proj-xcode/PowerAuth2/private/PA2PrivateEncryptorFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ - (PowerAuthCoreEciesEncryptor*) encryptorForScope:(PowerAuthCoreEciesEncryptorS
return [[_sessionProvider readTaskWithSession:^PA2Result<PowerAuthCoreEciesEncryptor*>* _Nullable(PowerAuthCoreSession * _Nonnull session) {
// Prepare data required for encryptor construction
NSString * activationId = nil;
NSString * temporaryKeyId = [session publicKeyIdForEciesScope:scope];
if (!temporaryKeyId) {
return [PA2Result failure:PA2MakeError(PowerAuthErrorCode_Encryption, @"Temporary key for ECIES is not set")];
}
PowerAuthCoreSignatureUnlockKeys * unlockKeys = nil;
if (scope == PowerAuthCoreEciesEncryptorScope_Activation) {
// For activation scope, also prepare activation ID and possession unlock key.
Expand All @@ -84,7 +88,7 @@ - (PowerAuthCoreEciesEncryptor*) encryptorForScope:(PowerAuthCoreEciesEncryptorS
unlockKeys = [[PowerAuthCoreSignatureUnlockKeys alloc] init];
unlockKeys.possessionUnlockKey = _deviceRelatedKey;
}
// Prepare the rest of information required for o
// Prepare the rest of information required for encryptor creation
NSData * sharedInfo1Data = [sharedInfo1 dataUsingEncoding:NSUTF8StringEncoding];
NSString * applicationKey = session.applicationKey;
// Now create the encryptor
Expand All @@ -96,6 +100,7 @@ - (PowerAuthCoreEciesEncryptor*) encryptorForScope:(PowerAuthCoreEciesEncryptorS
}
// And assign the associated metadata
encryptor.associatedMetaData = [[PowerAuthCoreEciesMetaData alloc] initWithApplicationKey:applicationKey
temporaryKeyId:temporaryKeyId
activationIdentifier:activationId];
return [PA2Result success:encryptor];
}] extractResult:error];
Expand Down
Loading

0 comments on commit af3938f

Please sign in to comment.