use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class TPMAttestationStatementValidator method validateX5c.
private void validateX5c(TPMAttestationStatement attestationStatement, TPMSAttest certInfo, AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData) {
// noinspection ConstantConditions as null check is already done in validateTPMAttestationStatementNull
X509Certificate aikCert = attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate();
// / Verify the sig is a valid signature over certInfo using the attestation public key in aikCert with the algorithm specified in alg.
String jcaName = getJcaName(attestationStatement.getAlg());
Signature certInfoSignature = SignatureUtil.createSignature(jcaName);
try {
certInfoSignature.initVerify(aikCert.getPublicKey());
certInfoSignature.update(certInfo.getBytes());
if (!certInfoSignature.verify(attestationStatement.getSig())) {
throw new BadAttestationStatementException("hash of certInfo doesn't match with sig.");
}
} catch (SignatureException | InvalidKeyException e) {
throw new BadAttestationStatementException("Failed to validate the signature.", e);
}
// / Verify that aikCert meets the requirements in §8.3.1 TPM Attestation Statement Certificate Requirements.
validateAikCert(aikCert);
// / If aikCert contains an extension with OID 1 3 6 1 4 1 45724 1 1 4 (id-fido-gen-ce-aaguid) verify that the value of this extension matches the aaguid in authenticatorData.
byte[] aaguidBytes = aikCert.getExtensionValue(ID_FIDO_GEN_CE_AAGUID);
// noinspection ConstantConditions as null check is already done in caller
if (aaguidBytes != null && !Objects.equals(new AAGUID(aaguidBytes), authenticatorData.getAttestedCredentialData().getAaguid())) {
throw new BadAttestationStatementException("AAGUID in aikCert doesn't match with that in authenticatorData");
}
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class FIDOU2FAttestationStatementValidator method validateAttestationStatement.
void validateAttestationStatement(@NonNull FIDOU2FAttestationStatement attestationStatement) {
if (attestationStatement.getX5c().size() != 1) {
throw new BadAttestationStatementException("FIDO-U2F attestation statement must have only one certificate.");
}
PublicKey publicKey = attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate().getPublicKey();
validatePublicKey(publicKey);
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class AndroidKeyAttestationStatementValidator method validate.
@Override
@NonNull
public AttestationType validate(@NonNull CoreRegistrationObject registrationObject) {
AssertUtil.notNull(registrationObject, "registrationObject must not be null");
if (!supports(registrationObject)) {
throw new IllegalArgumentException(String.format("Specified format '%s' is not supported by %s.", registrationObject.getAttestationObject().getFormat(), this.getClass().getName()));
}
AndroidKeyAttestationStatement attestationStatement = (AndroidKeyAttestationStatement) registrationObject.getAttestationObject().getAttestationStatement();
validateAttestationStatementNotNull(attestationStatement);
if (attestationStatement.getX5c().isEmpty()) {
throw new BadAttestationStatementException("No attestation certificate is found in android key attestation statement.");
}
// / Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
// / Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash using the public key in the first certificate in x5c with the algorithm specified in alg.
validateSignature(registrationObject);
// / Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData.
PublicKey publicKeyInEndEntityCert = attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate().getPublicKey();
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData = registrationObject.getAttestationObject().getAuthenticatorData();
// noinspection ConstantConditions as null check is already done in caller
PublicKey publicKeyInCredentialData = authenticatorData.getAttestedCredentialData().getCOSEKey().getPublicKey();
if (!publicKeyInEndEntityCert.equals(publicKeyInCredentialData)) {
throw new PublicKeyMismatchException("The public key in the first certificate in x5c doesn't matches the credentialPublicKey in the attestedCredentialData in authenticatorData.");
}
byte[] clientDataHash = registrationObject.getClientDataHash();
keyDescriptionValidator.validate(attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate(), clientDataHash, teeEnforcedOnly);
return AttestationType.BASIC;
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class PackedAttestationStatementValidator method validateX5c.
@SuppressWarnings("SameReturnValue")
@NonNull
private AttestationType validateX5c(@NonNull CoreRegistrationObject registrationObject, @NonNull PackedAttestationStatement attestationStatement, @NonNull byte[] sig, @NonNull COSEAlgorithmIdentifier alg, @NonNull byte[] attrToBeSigned) {
if (attestationStatement.getX5c() == null || attestationStatement.getX5c().isEmpty()) {
throw new BadAttestationStatementException("No attestation certificate is found in packed attestation statement.");
}
// using the attestation public key in x5c with the algorithm specified in alg.
if (!verifySignature(attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate().getPublicKey(), alg, sig, attrToBeSigned)) {
throw new BadSignatureException("`sig` in attestation statement is not valid signature over the concatenation of authenticatorData and clientDataHash.");
}
// Verify that x5c meets the requirements in §8.2.1 Packed attestation statement certificate requirements.
attestationStatement.getX5c().getEndEntityAttestationCertificate().validate();
// If x5c contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) verify that
// the value of this extension matches the aaguid in authenticatorData.
X509Certificate attestationCertificate = attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate();
AAGUID aaguidInCertificate = extractAAGUIDFromAttestationCertificate(attestationCertificate);
// noinspection ConstantConditions as null check is already done in caller
AAGUID aaguid = registrationObject.getAttestationObject().getAuthenticatorData().getAttestedCredentialData().getAaguid();
if (aaguidInCertificate != AAGUID.NULL && !Objects.equals(aaguidInCertificate, aaguid)) {
throw new BadAttestationStatementException("AAGUID in attestation certificate doesn't match the AAGUID in authenticatorData.");
}
// If successful, return attestation type BASIC and attestation trust path x5c.
return AttestationType.BASIC;
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class TPMAttestationStatementValidator method validate.
@Override
@NonNull
public AttestationType validate(@NonNull CoreRegistrationObject registrationObject) {
AssertUtil.notNull(registrationObject, "registrationObject must not be null");
if (!supports(registrationObject)) {
throw new IllegalArgumentException("Specified format is not supported by " + this.getClass().getName());
}
TPMAttestationStatement attestationStatement = (TPMAttestationStatement) registrationObject.getAttestationObject().getAttestationStatement();
validateAttestationStatementNotNull(attestationStatement);
if (!attestationStatement.getVer().equals(TPMAttestationStatement.VERSION_2_0)) {
throw new BadAttestationStatementException("TPM version is not supported.");
}
TPMSAttest certInfo = attestationStatement.getCertInfo();
TPMTPublic pubArea = attestationStatement.getPubArea();
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData = registrationObject.getAttestationObject().getAuthenticatorData();
// / Verify that the public key specified by the parameters and unique fields of pubArea is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData.
validatePublicKeyEquality(pubArea, authenticatorData);
// / Concatenate authenticatorData and clientDataHash to form attToBeSigned.
byte[] attToBeSigned = getAttToBeSigned(registrationObject);
// / Verify that magic is set to TPM_GENERATED_VALUE.
if (certInfo.getMagic() != TPMGenerated.TPM_GENERATED_VALUE) {
throw new BadAttestationStatementException("magic must be TPM_GENERATED_VALUE");
}
// / Verify that type is set to TPM_ST_ATTEST_CERTIFY.
if (certInfo.getType() != TPMISTAttest.TPM_ST_ATTEST_CERTIFY) {
throw new BadAttestationStatementException("type must be TPM_ST_ATTEST_CERTIFY");
}
// / Verify that extraData is set to the hash of attToBeSigned using the hash algorithm employed in "alg".
COSEAlgorithmIdentifier alg = attestationStatement.getAlg();
MessageDigest messageDigest = getMessageDigest(alg);
byte[] hash = messageDigest.digest(attToBeSigned);
// As hash is public data(not secret data) to client side, there is no risk of timing attack and it is OK to use `Arrays.equals` instead of `MessageDigest.isEqual`
if (!Arrays.equals(certInfo.getExtraData(), hash)) {
throw new BadAttestationStatementException("extraData must be equals to the hash of attToBeSigned");
}
// / Verify that attested contains a TPMS_CERTIFY_INFO structure as specified in [TPMv2-Part2] section 10.12.3,
// / whose name field contains a valid Name for pubArea, as computed using the algorithm in the nameAlg field of
// / pubArea using the procedure specified in [TPMv2-Part1] section 16.
TPMSCertifyInfo certifyInfo = (TPMSCertifyInfo) certInfo.getAttested();
TPMIAlgHash hashAlg = certifyInfo.getName().getHashAlg();
String algJcaName;
algJcaName = getAlgJcaName(hashAlg);
byte[] pubAreaDigest = MessageDigestUtil.createMessageDigest(algJcaName).digest(pubArea.getBytes());
// there is no need to prevent timing attack and it is OK to use `Arrays.equals` instead of `MessageDigest.isEqual` here.
if (!Arrays.equals(pubAreaDigest, certifyInfo.getName().getDigest())) {
throw new BadAttestationStatementException("hash of `attested` doesn't match with name field of certifyInfo");
}
// / If x5c is present, this indicates that the attestation type is not ECDAA. In this case:
if (attestationStatement.getX5c() != null) {
validateX5c(attestationStatement, certInfo, authenticatorData);
// / If successful, return implementation-specific values representing attestation type AttCA and attestation trust path x5c.
return AttestationType.ATT_CA;
}
throw new BadAttestationStatementException("`x5c` or `ecdaaKeyId` must be present.");
}
Aggregations