Skip to content

Commit

Permalink
Fix #1413: FIDO2: Unify AAGUID format (#1416)
Browse files Browse the repository at this point in the history
  • Loading branch information
romanstrobl authored Mar 19, 2024
1 parent 8ab1817 commit 84e7ebc
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.powerauth.fido2.rest.model.request.RegistrationRequest;
import com.wultra.powerauth.fido2.rest.model.response.RegistrationResponse;
import com.wultra.powerauth.fido2.service.Fido2AuthenticatorService;
import com.wultra.powerauth.fido2.service.model.Fido2Authenticator;
import com.wultra.security.powerauth.client.model.enumeration.ActivationStatus;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.nio.ByteBuffer;
import java.util.*;

/**
* Converter class for registration related objects.
Expand All @@ -46,8 +43,6 @@
@Slf4j
public class RegistrationConverter {

private final Fido2AuthenticatorService fido2AuthenticatorService;

/**
* Convert registration challenge to authenticator detail.
* @param challenge Registration challenge.
Expand Down Expand Up @@ -113,9 +108,20 @@ private Map<String, Object> convertExtras(RegistrationRequest requestObject) thr
params.put("origin", authenticatorParameters.getResponse().getClientDataJSON().getOrigin());
params.put("topOrigin", authenticatorParameters.getResponse().getClientDataJSON().getTopOrigin());
params.put("isCrossOrigin", authenticatorParameters.getResponse().getClientDataJSON().isCrossOrigin());
params.put("aaguid", authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid());
final byte[] aaguidBytes = authenticatorParameters.getResponse().getAttestationObject().getAuthData().getAttestedCredentialData().getAaguid();
params.put("aaguid", bytesToUUID(aaguidBytes));
params.put("transports", authenticatorParameters.getResponse().getTransports());
return params;
}

public UUID bytesToUUID(byte[] bytes) {
if (bytes == null) {
return null;
}
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
long mostSigBits = byteBuffer.getLong();
long leastSigBits = byteBuffer.getLong();
return new UUID(mostSigBits, leastSigBits);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,13 @@ public class Fido2AuthenticatorService {
* @return Fido2Authenticator with registered details.
*/
@Cacheable("fido2-authenticators-cache")
public Fido2Authenticator findByAaguid(final byte[] aaguid) {
final UUID uuid = uuidFromBytes(aaguid);
if (uuid == null) {
public Fido2Authenticator findByAaguid(final UUID aaguid) {
if (aaguid == null) {
return unknownAuthenticator();
}
return fido2AuthenticatorRepository.findById(uuid.toString())
return fido2AuthenticatorRepository.findById(aaguid.toString())
.map(Fido2AuthenticatorService::convert)
.orElseGet(() -> unknownAuthenticator(uuid));
.orElseGet(() -> unknownAuthenticator(aaguid));
}

private static Fido2Authenticator convert(final Fido2AuthenticatorEntity entity) {
Expand All @@ -74,13 +73,4 @@ private static Fido2Authenticator unknownAuthenticator(final UUID aaguid) {
return new Fido2Authenticator(aaguid, UNKNOWN_AUTHENTICATOR_DESCRIPTION, UNKNOWN_AUTHENTICATOR_SIGNATURE_TYPE);
}

private static UUID uuidFromBytes(final byte[] bytes) {
if (bytes == null || bytes.length != 16) { // strange byte array length, UUID requires 16 bytes
logger.debug("Invalid byte length provided for UUID: {}", bytes);
return null;
}
final ByteBuffer bb = ByteBuffer.wrap(bytes);
return new UUID(bb.getLong(), bb.getLong());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public RegistrationResponse register(RegistrationRequest requestObject) throws E
logger.info("No signature verification on registration");
}

final Fido2Authenticator model = fido2AuthenticatorService.findByAaguid(aaguid);
final Fido2Authenticator model = fido2AuthenticatorService.findByAaguid(registrationConverter.bytesToUUID(aaguid));
final RegistrationChallenge challenge = registrationProvider.findRegistrationChallengeByValue(applicationId, challengeValue);
final AuthenticatorDetail authenticator = registrationConverter.convert(challenge, requestObject, model, cryptographyService.publicKeyToBytes(attestedCredentialData.getPublicKeyObject()))
.orElseThrow(() -> new Fido2AuthenticationFailedException("Invalid request"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void testFindByAaguid() {
when(fido2AuthenticatorRepository.findById(aaguid.toString()))
.thenReturn(Optional.of(entity));

final Fido2Authenticator authenticator = tested.findByAaguid(toBytes(aaguid));
final Fido2Authenticator authenticator = tested.findByAaguid(aaguid);
assertEquals(aaguid, authenticator.aaguid());
assertEquals("My FIDO2 Authenticator", authenticator.description());
assertEquals(SignatureType.POSSESSION, authenticator.signatureType());
Expand All @@ -73,7 +73,7 @@ void testFindByAaguid_unknown() {
when(fido2AuthenticatorRepository.findById(aaguid.toString()))
.thenReturn(Optional.empty());

final Fido2Authenticator authenticator = tested.findByAaguid(toBytes(aaguid));
final Fido2Authenticator authenticator = tested.findByAaguid(aaguid);
assertEquals(aaguid, authenticator.aaguid());
assertEquals("Unknown FIDO2 Authenticator", authenticator.description());
assertEquals(SignatureType.POSSESSION, authenticator.signatureType());
Expand All @@ -87,11 +87,4 @@ void testFindByAaguid_missingAaguid() {
assertEquals(SignatureType.POSSESSION, authenticator.signatureType());
}

private static byte[] toBytes(final UUID aaguid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(aaguid.getMostSignificantBits());
bb.putLong(aaguid.getLeastSignificantBits());
return bb.array();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class ActivationRecordEntity implements Serializable {
@Column(name = "activation_name")
private String activationName;

@Column(name = "extras")
@Column(name = "extras", columnDefinition = "CLOB")
private String extras;

@Column(name = "protocol")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,9 @@ private void handleStatus(OperationStatus status) throws Fido2AuthenticationFail
}

private SignatureType supportedSignatureType(AuthenticatorDetail authenticatorDetail, boolean userVerified) {
final String aaguidBase64 = (String) authenticatorDetail.getExtras().get("aaguid");
if (aaguidBase64 != null) {
final byte[] aaguid = Base64.getDecoder().decode(aaguidBase64);
return userVerified ? fido2AuthenticatorService.findByAaguid(aaguid).signatureType() : SignatureType.POSSESSION;
final String aaguid = (String) authenticatorDetail.getExtras().get("aaguid");
if (aaguid != null) {
return userVerified ? fido2AuthenticatorService.findByAaguid(UUID.fromString(aaguid)).signatureType() : SignatureType.POSSESSION;
} else {
return SignatureType.POSSESSION;
}
Expand Down

0 comments on commit 84e7ebc

Please sign in to comment.