Skip to content

Commit

Permalink
Fix #218: Add test of proximity anti-fraud check at offline mode
Browse files Browse the repository at this point in the history
  • Loading branch information
banterCZ committed Jul 21, 2023
1 parent d4502a5 commit 1737a7f
Showing 1 changed file with 88 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import com.wultra.security.powerauth.client.PowerAuthClient;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
import com.wultra.security.powerauth.client.model.request.CreatePersonalizedOfflineSignaturePayloadRequest;
import com.wultra.security.powerauth.client.model.request.InitActivationRequest;
import com.wultra.security.powerauth.client.model.request.VerifyOfflineSignatureRequest;
import com.wultra.security.powerauth.client.model.response.*;
import com.wultra.security.powerauth.configuration.PowerAuthTestConfiguration;
import io.getlime.core.rest.model.base.response.ErrorResponse;
Expand Down Expand Up @@ -573,6 +575,92 @@ void signatureOfflinePersonalizedValidTest() throws Exception {
CounterUtil.incrementCounter(model);
}

@Test
void testSignatureOfflinePersonalizedProximityCheckValid() throws Exception {
testSignatureOfflinePersonalizedProximityCheck(true);
}
@Test
void testSignatureOfflinePersonalizedProximityCheckInvalid() throws Exception {
testSignatureOfflinePersonalizedProximityCheck(false);
}

private void testSignatureOfflinePersonalizedProximityCheck(final boolean expectedResult) throws Exception {
final CreatePersonalizedOfflineSignaturePayloadRequest request = new CreatePersonalizedOfflineSignaturePayloadRequest();
request.setActivationId(config.getActivationIdV3());
request.setData(offlineData);
request.setProximityCheck(new CreatePersonalizedOfflineSignaturePayloadRequest.ProximityCheck());
request.getProximityCheck().setSeed("LtxE0f0RWNx3hy7ISjUPWA==");
request.getProximityCheck().setStepLength(30);

final CreatePersonalizedOfflineSignaturePayloadResponse offlineResponse = powerAuthClient.createPersonalizedOfflineSignaturePayload(request);
final String nonce = offlineResponse.getNonce();
final String offlineDataResponse = offlineResponse.getOfflineData();

final String[] parts = offlineDataResponse.split("\n");

// Extract last line which contains information about key and ECDSA signature
final String lastLine = parts[parts.length - 1];

// 1 = KEY_SERVER_PRIVATE was used to sign data (personalized offline signature)
assertEquals("1", lastLine.substring(0, 1));

// The remainder of last line is Base64 encoded ECDSA signature
final String ecdsaSignature = lastLine.substring(1);
final byte[] serverPublicKeyBytes = Base64.getDecoder().decode((String) model.getResultStatusObject().get("serverPublicKey"));
final ECPublicKey serverPublicKey = (ECPublicKey) config.getKeyConvertor().convertBytesToPublicKey(serverPublicKeyBytes);

// Prepare offline data without signature
final String offlineDataWithoutSignature = offlineDataResponse.substring(0, offlineDataResponse.length() - ecdsaSignature.length());

// Validate ECDSA signature of data using server public key
assertTrue(signatureUtils.validateECDSASignature(offlineDataWithoutSignature.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(ecdsaSignature), serverPublicKey));

// Prepare data for PowerAuth signature
final String proximityTotp = expectedResult ? parts[5] : "11111111";
final String dataForSignature = operationId + "&" + operationData + "&" + proximityTotp;

// Prepare normalized data for signature
final String signatureBaseString = PowerAuthHttpBody.getSignatureBaseString("POST", "/operation/authorize/offline", Base64.getDecoder().decode(nonce), dataForSignature.getBytes(StandardCharsets.UTF_8));

// Prepare keys
byte[] signaturePossessionKeyBytes = Base64.getDecoder().decode((String) model.getResultStatusObject().get("signaturePossessionKey"));
byte[] signatureKnowledgeKeySalt = Base64.getDecoder().decode((String) model.getResultStatusObject().get("signatureKnowledgeKeySalt"));
byte[] signatureKnowledgeKeyEncryptedBytes = Base64.getDecoder().decode((String) model.getResultStatusObject().get("signatureKnowledgeKeyEncrypted"));

// Get the signature keys
final SecretKey signaturePossessionKey = config.getKeyConvertor().convertBytesToSharedSecretKey(signaturePossessionKeyBytes);
final SecretKey signatureKnowledgeKey = EncryptedStorageUtil.getSignatureKnowledgeKey(config.getPassword().toCharArray(), signatureKnowledgeKeyEncryptedBytes, signatureKnowledgeKeySalt, new KeyGenerator());

// Put keys into a list
final List<SecretKey> signatureKeys = new ArrayList<>();
signatureKeys.add(signaturePossessionKey);
signatureKeys.add(signatureKnowledgeKey);

// Calculate signature of normalized signature base string with 'offline' as application secret
final String signature = signatureUtils.computePowerAuthSignature((signatureBaseString + "&offline").getBytes(StandardCharsets.UTF_8), signatureKeys, CounterUtil.getCtrData(model, stepLogger), SignatureConfiguration.decimal());

final VerifyOfflineSignatureRequest verifyRequest = new VerifyOfflineSignatureRequest();
verifyRequest.setActivationId(config.getActivationIdV3());
verifyRequest.setData(signatureBaseString);
verifyRequest.setSignature(signature);
verifyRequest.setAllowBiometry(true);
verifyRequest.setProximityCheck(new VerifyOfflineSignatureRequest.ProximityCheck());
verifyRequest.getProximityCheck().setSeed("LtxE0f0RWNx3hy7ISjUPWA==");
verifyRequest.getProximityCheck().setStepLength(30);
verifyRequest.getProximityCheck().setStepCount(2);

final VerifyOfflineSignatureResponse signatureResponse = powerAuthClient.verifyOfflineSignature(verifyRequest);
assertEquals(expectedResult, signatureResponse.isSignatureValid());
assertEquals(config.getActivationIdV3(), signatureResponse.getActivationId());
assertEquals(ActivationStatus.ACTIVE, signatureResponse.getActivationStatus());
final BigInteger expectedRemainingAttempts = BigInteger.valueOf(expectedResult ? 5 : 4);
assertEquals(expectedRemainingAttempts, signatureResponse.getRemainingAttempts());
assertEquals(SignatureType.POSSESSION_KNOWLEDGE, signatureResponse.getSignatureType());
assertEquals(config.getApplicationId(), signatureResponse.getApplicationId());

CounterUtil.incrementCounter(model);
}

@Test
void signatureOfflinePersonalizedInvalidTest() throws Exception {

Expand Down

0 comments on commit 1737a7f

Please sign in to comment.