extractPublicKeyFromRegistration static method
Extracts the secp256r1 public key from a WebAuthn registration response using multiple fallback strategies.
The method tries three strategies in order:
- Direct public key: when
publicKeyis provided, validate it as a 65-byte uncompressed secp256r1 key (0x04prefix) and verify the point is on the curve. - Authenticator data parsing: when
authenticatorDatais provided, parse the attested credential data structure to extractX/Ycoordinates from the COSE key. - Attestation object pattern matching: when
attestationObjectis provided, search for the COSE key prefix pattern and extractX/Ycoordinates.
At least one of the three parameters must be non-null. Compressed
keys (0x02/0x03 prefix) are not supported and cause the method
to throw immediately rather than fall through to other strategies.
Throws SmartAccountInvalidInput when a compressed-key prefix is detected, when no extraction source is provided, or when all strategies fail.
Implementation
static Uint8List extractPublicKeyFromRegistration({
Uint8List? publicKey,
Uint8List? authenticatorData,
Uint8List? attestationObject,
}) {
if (publicKey != null && publicKey.isNotEmpty) {
final candidate =
publicKey.length > SmartAccountConstants.secp256r1PublicKeySize
? Uint8List.fromList(publicKey.sublist(
publicKey.length -
SmartAccountConstants.secp256r1PublicKeySize,
publicKey.length,
))
: Uint8List.fromList(publicKey);
if (candidate.length ==
SmartAccountConstants.secp256r1PublicKeySize &&
candidate[0] == SmartAccountConstants.uncompressedPubkeyPrefix) {
_validatePointOnCurve(
Uint8List.sublistView(candidate, 1, 33),
Uint8List.sublistView(candidate, 33, 65),
);
return candidate;
}
if (candidate[0] == 0x02 || candidate[0] == 0x03) {
final prefixHex = candidate[0].toRadixString(16).padLeft(2, '0');
throw SmartAccountValidationException.invalidInput(
'publicKey',
'Compressed secp256r1 key format (prefix 0x$prefixHex) is not '
'supported; the platform must provide an uncompressed key '
'(0x04 prefix)',
);
}
// Non-key data (e.g. CBOR/attestation bytes); fall through to the
// remaining strategies.
}
if (authenticatorData != null) {
final extracted =
extractPublicKeyFromAuthenticatorData(authenticatorData);
if (extracted != null) {
return extracted;
}
}
if (attestationObject != null) {
return extractPublicKeyFromAttestationObject(attestationObject);
}
throw SmartAccountValidationException.invalidInput(
'registration',
'Could not extract public key from attestation response: no valid '
'publicKey, authenticatorData, or attestationObject provided',
);
}