extractPublicKeyFromRegistration static method

Uint8List extractPublicKeyFromRegistration({
  1. Uint8List? publicKey,
  2. Uint8List? authenticatorData,
  3. Uint8List? attestationObject,
})

Extracts the secp256r1 public key from a WebAuthn registration response using multiple fallback strategies.

The method tries three strategies in order:

  1. Direct public key: when publicKey is provided, validate it as a 65-byte uncompressed secp256r1 key (0x04 prefix) and verify the point is on the curve.
  2. Authenticator data parsing: when authenticatorData is provided, parse the attested credential data structure to extract X/Y coordinates from the COSE key.
  3. Attestation object pattern matching: when attestationObject is provided, search for the COSE key prefix pattern and extract X/Y coordinates.

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',
  );
}