Skip to content

Commit

Permalink
PYIC-7571: Track verifier use (#2641)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wynndow authored Oct 30, 2024
2 parents fc4f557 + a968b1d commit 62add08
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.crypto.impl.ECDSA;
Expand Down Expand Up @@ -33,6 +32,7 @@
import uk.gov.di.ipv.core.library.service.ConfigService;
import uk.gov.di.ipv.core.reconcilemigratedvcs.domain.ReconciliationReport;
import uk.gov.di.ipv.core.reconcilemigratedvcs.domain.Request;
import uk.gov.di.ipv.core.reconcilemigratedvcs.domain.VerifierAndUseCount;

import java.text.ParseException;
import java.util.ArrayList;
Expand All @@ -42,6 +42,7 @@
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import static com.nimbusds.jose.JWSAlgorithm.ES256;
import static com.nimbusds.jose.jwk.KeyType.EC;
Expand Down Expand Up @@ -73,7 +74,7 @@ public class ReconcileMigratedVcsHandler implements RequestHandler<Request, Reco
private ConcurrentSkipListSet<String> processedIdentities;
private AtomicBoolean timeoutClose;
private Map<String, Cri> criIssuersMap;
private Map<Cri, List<JWSVerifier>> historicSigningKeyVerifiers;
private Map<Cri, List<VerifierAndUseCount>> historicSigningKeyVerifiers;

public ReconcileMigratedVcsHandler(
ConfigService configService,
Expand Down Expand Up @@ -174,6 +175,7 @@ public ReconciliationReport handleRequest(Request request, Context context) {
unprocessedIdentities.removeAll(processedIdentities);
reconciliationReport.setUnprocessedHashedUserIds(
unprocessedIdentities.stream().map(DigestUtils::sha256Hex).toList());
reconciliationReport.setVerifierUse(historicSigningKeyVerifiers);
logReport();
}

Expand Down Expand Up @@ -220,12 +222,12 @@ private void reconcileIdentity(String userId, Context context) {
// Compare VCs
if (!evcsVcsStrings.equals(tacticalVcsStrings)) {
logVcDifferences(evcsVcsStrings, tacticalVcsStrings, hashedUserId);
reconciliationReport.incrementDifferenceInVcs(hashedUserId);
reconciliationReport.incrementIdentitiesWithDifferentVcs(hashedUserId);
reconciliationReport.incrementIdentitiesFullyProcessed();
processedIdentities.add(userId);
return;
} else {
reconciliationReport.incrementVcsMatch();
reconciliationReport.incrementIdentitiesWithMatchingVcs(evcsVcsStrings.size());
}

if (reconciliationReport.isCheckSignatures() || reconciliationReport.isCheckP2()) {
Expand Down Expand Up @@ -325,9 +327,9 @@ private synchronized void refreshEvcsClientIfNeeded() {
private boolean validSignature(VerifiableCredential vc, String hashedUserId)
throws JOSEException, ParseException {
// try all known keys until VC is validated, or return false
for (var verifier : historicSigningKeyVerifiers.get(vc.getCri())) {
for (var verifierAndUseCount : historicSigningKeyVerifiers.get(vc.getCri())) {
SignedJWT formattedVc;
if (verifier instanceof ECDSAVerifier) {
if (verifierAndUseCount.getVerifier() instanceof ECDSAVerifier) {
try {
formattedVc = transcodeSignatureIfDerFormat(vc.getSignedJwt());
} catch (JOSEException | ParseException e) {
Expand All @@ -338,7 +340,8 @@ private boolean validSignature(VerifiableCredential vc, String hashedUserId)
formattedVc = vc.getSignedJwt();
}

if (formattedVc.verify(verifier)) {
if (formattedVc.verify(verifierAndUseCount.getVerifier())) {
verifierAndUseCount.getUseCount().incrementAndGet();
return true;
}
}
Expand Down Expand Up @@ -430,19 +433,22 @@ private void logReport() {
}
}

private Map<Cri, List<JWSVerifier>> getHistoricSigningKeyVerifiers()
private Map<Cri, List<VerifierAndUseCount>> getHistoricSigningKeyVerifiers()
throws ParseException, JOSEException {
Map<Cri, List<JWSVerifier>> verifiers = new EnumMap<>(Cri.class);
Map<Cri, List<VerifierAndUseCount>> verifiers = new EnumMap<>(Cri.class);
for (var cri : Cri.values()) {
try {
for (var publicKey : configService.getHistoricSigningKeys(cri.getId())) {
var parsedJwk = JWK.parse(publicKey);
verifiers
.computeIfAbsent(cri, k -> new ArrayList<>())
.add(
parsedJwk.getKeyType() == EC
? new ECDSAVerifier(parsedJwk.toECKey())
: new RSASSAVerifier(parsedJwk.toRSAKey()));
new VerifierAndUseCount(
parsedJwk.getKeyType() == EC
? new ECDSAVerifier(parsedJwk.toECKey())
: new RSASSAVerifier(parsedJwk.toRSAKey()),
publicKey,
new AtomicInteger(0)));
}
} catch (ConfigParameterNotFoundException e) {
LOGGER.warn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import lombok.Getter;
import lombok.Setter;
import uk.gov.di.ipv.core.library.annotations.ExcludeFromGeneratedCoverageReport;
import uk.gov.di.ipv.core.library.domain.Cri;
import uk.gov.di.ipv.core.library.domain.VerifiableCredential;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Getter
@ExcludeFromGeneratedCoverageReport
Expand All @@ -16,8 +18,9 @@ public class ReconciliationReport {
private final boolean checkP2;
private final int batchSize;
private int identitiesFullyProcessed;
private int identitiesWithMatchingVcs;
private int vcsMatchedCount;
private int vcsDifferentCount;
private int identitiesWithDifferentVcsCount;
private int identityWithValidSignaturesCount;
private int identityWithInvalidSignaturesCount;
private int invalidVcSignatureCount;
Expand All @@ -35,8 +38,9 @@ public class ReconciliationReport {
private final List<String> identityWithAnyInvalidSignaturesHashedUserIds = new ArrayList<>();
private final List<String> invalidVcSignatureHashedUserIdAndCri = new ArrayList<>();
private final List<String> failedToAttainP2HashedUserIds = new ArrayList<>();
private final List<String> differenceInVcsHashedUserIds = new ArrayList<>();
private final List<String> identitiesWithDifferentVcsHashedUserIds = new ArrayList<>();
@Setter private List<String> unprocessedHashedUserIds = new ArrayList<>();
@Setter private Map<Cri, List<VerifierAndUseCount>> verifierUse;

public ReconciliationReport(Request request) {
batchId = request.batchId();
Expand All @@ -49,8 +53,14 @@ public synchronized void incrementIdentitiesFullyProcessed() {
identitiesFullyProcessed++;
}

public synchronized void incrementVcsMatch() {
vcsMatchedCount++;
public synchronized void incrementIdentitiesWithMatchingVcs(int vcCount) {
identitiesWithMatchingVcs++;
vcsMatchedCount += vcCount;
}

public synchronized void incrementIdentitiesWithDifferentVcs(String hashedUserId) {
identitiesWithDifferentVcsCount++;
identitiesWithDifferentVcsHashedUserIds.add(hashedUserId);
}

public synchronized void incrementFailedEvcsRead(String hashedUserId) {
Expand Down Expand Up @@ -99,9 +109,4 @@ public synchronized void incrementFailedToAttainP2(String hashedUserId) {
public synchronized void incrementSuccessfullyMatchedP2Count() {
successfullyAttainedP2Count++;
}

public synchronized void incrementDifferenceInVcs(String hashedUserId) {
vcsDifferentCount++;
differenceInVcsHashedUserIds.add(hashedUserId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package uk.gov.di.ipv.core.reconcilemigratedvcs.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nimbusds.jose.JWSVerifier;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.concurrent.atomic.AtomicInteger;

@Getter
@AllArgsConstructor
public class VerifierAndUseCount {
@JsonIgnore private final JWSVerifier verifier;
private final String publicKey;
private final AtomicInteger useCount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ void handlerShouldHandleMatchingVcs() throws Exception {
assertEquals("vcs-match-batch-id", reconciliationReport.getBatchId());
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(1, reconciliationReport.getIdentitiesWithMatchingVcs());
assertEquals(3, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertFalse(reconciliationReport.isCheckSignatures());
assertFalse(reconciliationReport.isCheckP2());
}
Expand Down Expand Up @@ -122,7 +123,7 @@ void handlerShouldHandleNonMatchingVcs() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(0, reconciliationReport.getVcsMatchedCount());
assertEquals(1, reconciliationReport.getVcsDifferentCount());
assertEquals(1, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertFalse(reconciliationReport.isCheckSignatures());
assertFalse(reconciliationReport.isCheckP2());
}
Expand Down Expand Up @@ -151,7 +152,7 @@ void handlerShouldHandleMissingVcs() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(0, reconciliationReport.getVcsMatchedCount());
assertEquals(1, reconciliationReport.getVcsDifferentCount());
assertEquals(1, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertFalse(reconciliationReport.isCheckSignatures());
assertFalse(reconciliationReport.isCheckP2());
}
Expand Down Expand Up @@ -193,8 +194,9 @@ void handlerShouldHandleValidateSignatures() throws Exception {
assertEquals("vcs-valid-sig-batch-id", reconciliationReport.getBatchId());
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(1, reconciliationReport.getIdentitiesWithMatchingVcs());
assertEquals(2, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals(1, reconciliationReport.getIdentityWithValidSignaturesCount());
assertEquals(0, reconciliationReport.getIdentityWithInvalidSignaturesCount());
assertEquals(0, reconciliationReport.getInvalidVcSignatureCount());
Expand Down Expand Up @@ -224,7 +226,7 @@ void handlerShouldHandleInvalidSignatures() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals(0, reconciliationReport.getIdentityWithValidSignaturesCount());
assertEquals(1, reconciliationReport.getIdentityWithInvalidSignaturesCount());
assertEquals(1, reconciliationReport.getInvalidVcSignatureCount());
Expand Down Expand Up @@ -254,7 +256,7 @@ void handlerShouldCheckSignaturesWithAllKeys() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals(1, reconciliationReport.getIdentityWithValidSignaturesCount());
assertEquals(0, reconciliationReport.getIdentityWithInvalidSignaturesCount());
assertEquals(0, reconciliationReport.getInvalidVcSignatureCount());
Expand Down Expand Up @@ -287,7 +289,7 @@ void handlerShouldHandleIdentityWithP2() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals(1, reconciliationReport.getIdentityWithValidSignaturesCount());
assertEquals(0, reconciliationReport.getIdentityWithInvalidSignaturesCount());
assertEquals(0, reconciliationReport.getInvalidVcSignatureCount());
Expand Down Expand Up @@ -317,7 +319,7 @@ void handlerShouldHandleIdentityWithNonP2() throws Exception {
assertEquals(1, reconciliationReport.getBatchSize());
assertEquals(1, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(1, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals(0, reconciliationReport.getIdentityWithValidSignaturesCount());
assertEquals(0, reconciliationReport.getIdentityWithInvalidSignaturesCount());
assertEquals(0, reconciliationReport.getInvalidVcSignatureCount());
Expand All @@ -344,8 +346,8 @@ void handlerShouldHandleLambdaTimeout() throws Exception {
assertEquals("timeout-batch-id", reconciliationReport.getBatchId());
assertEquals(2, reconciliationReport.getBatchSize());
assertEquals(2, reconciliationReport.getIdentitiesFullyProcessed());
assertEquals(2, reconciliationReport.getVcsMatchedCount());
assertEquals(0, reconciliationReport.getVcsDifferentCount());
assertEquals(2, reconciliationReport.getIdentitiesWithMatchingVcs());
assertEquals(0, reconciliationReport.getIdentitiesWithDifferentVcsCount());
assertEquals("Lambda close to timeout", reconciliationReport.getExitReason());
}

Expand Down

0 comments on commit 62add08

Please sign in to comment.