auth: publish passkey foundations preview
verify / verify (push) Successful in 3m40s

This commit is contained in:
2026-08-21 17:33:00 -04:00
parent fb6bbd0dad
commit bfe6cfd29e
230 changed files with 44547 additions and 17 deletions
+137
View File
@@ -0,0 +1,137 @@
package webauthncose
const (
keyCannotDisplay = "Cannot display key"
)
const ecCoordSize = 32
// COSEAlgorithmIdentifier is a number identifying a cryptographic algorithm. The algorithm identifiers SHOULD be values
// registered in the IANA COSE Algorithms registry [https://www.w3.org/TR/webauthn/#biblio-iana-cose-algs-reg], for
// instance, -7 for "ES256" and -257 for "RS256".
//
// Specification: §5.8.5. Cryptographic Algorithm Identifier (https://www.w3.org/TR/webauthn/#sctn-alg-identifier)
type COSEAlgorithmIdentifier int
const (
// AlgES256 ECDSA with SHA-256.
AlgES256 COSEAlgorithmIdentifier = -7
// AlgEdDSA EdDSA.
AlgEdDSA COSEAlgorithmIdentifier = -8
// AlgESP256 is ECDSA using P-256 curve with pre-hashed SHA-256 input.
AlgESP256 COSEAlgorithmIdentifier = -9
// AlgEd25519 is EdDSA using the Ed25519 curve specifically. Unlike [AlgEdDSA] which is the generic EdDSA
// identifier, this explicitly specifies the Ed25519 curve.
AlgEd25519 COSEAlgorithmIdentifier = -19
// AlgES384 ECDSA with SHA-384.
AlgES384 COSEAlgorithmIdentifier = -35
// AlgES512 ECDSA with SHA-512.
AlgES512 COSEAlgorithmIdentifier = -36
// AlgPS256 RSASSA-PSS with SHA-256.
AlgPS256 COSEAlgorithmIdentifier = -37
// AlgPS384 RSASSA-PSS with SHA-384.
AlgPS384 COSEAlgorithmIdentifier = -38
// AlgPS512 RSASSA-PSS with SHA-512.
AlgPS512 COSEAlgorithmIdentifier = -39
// AlgES256K is ECDSA using secp256k1 curve and SHA-256.
AlgES256K COSEAlgorithmIdentifier = -47
// AlgMLDSA44 is ML-DSA with parameter set ML-DSA-44 (FIPS 204).
AlgMLDSA44 COSEAlgorithmIdentifier = -48
// AlgMLDSA65 is ML-DSA with parameter set ML-DSA-65 (FIPS 204).
AlgMLDSA65 COSEAlgorithmIdentifier = -49
// AlgMLDSA87 is ML-DSA with parameter set ML-DSA-87 (FIPS 204).
AlgMLDSA87 COSEAlgorithmIdentifier = -50
// AlgESP384 is ECDSA using P-384 curve with pre-hashed SHA-384 input.
AlgESP384 COSEAlgorithmIdentifier = -51
// AlgESP512 is ECDSA using P-521 curve with pre-hashed SHA-512 input.
AlgESP512 COSEAlgorithmIdentifier = -52
// AlgRS256 RSASSA-PKCS1-v1_5 with SHA-256.
AlgRS256 COSEAlgorithmIdentifier = -257
// AlgRS384 RSASSA-PKCS1-v1_5 with SHA-384.
AlgRS384 COSEAlgorithmIdentifier = -258
// AlgRS512 RSASSA-PKCS1-v1_5 with SHA-512.
AlgRS512 COSEAlgorithmIdentifier = -259
// AlgRS1 RSASSA-PKCS1-v1_5 with SHA-1.
AlgRS1 COSEAlgorithmIdentifier = -65535
)
// COSEKeyType is The Key type derived from the IANA COSE AuthData.
type COSEKeyType int
const (
// KeyTypeReserved is a reserved value.
KeyTypeReserved COSEKeyType = iota
// OctetKey is an Octet Key.
OctetKey
// EllipticKey is an Elliptic Curve Public Key.
EllipticKey
// RSAKey is an RSA Public Key.
RSAKey
// Symmetric Keys.
Symmetric
// HSSLMS is the public key for HSS/LMS hash-based digital signature.
HSSLMS
// WalnutDSA is the public key for Walnut Digital Signature Algorithm.
WalnutDSA
// AKP is the key type for algorithm key pairs (i.e. ML-DSA).
AKP
)
// COSEEllipticCurve is an enumerator that represents the COSE Elliptic Curves.
//
// Specification: https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
type COSEEllipticCurve int
const (
// EllipticCurveReserved is the COSE EC Reserved value.
EllipticCurveReserved COSEEllipticCurve = iota
// P256 represents NIST P-256 also known as secp256r1.
P256
// P384 represents NIST P-384 also known as secp384r1.
P384
// P521 represents NIST P-521 also known as secp521r1.
P521
// X25519 for use w/ ECDH only.
X25519
// X448 for use w/ ECDH only.
X448
// Ed25519 for use w/ EdDSA only.
Ed25519
// Ed448 for use w/ EdDSA only.
Ed448
// Secp256k1 is the SECG secp256k1 curve.
Secp256k1
)
@@ -0,0 +1,10 @@
package webauthncose
import (
"crypto/ed25519"
"crypto/x509"
)
func marshalEd25519PublicKey(pub ed25519.PublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(pub)
}
@@ -0,0 +1,7 @@
package webauthncose
import "math/big"
type ECDSASignature struct {
R, S *big.Int
}
+13
View File
@@ -0,0 +1,13 @@
package webauthncose
import "sync/atomic"
var allowBERIntegers atomic.Bool
// SetExperimentalInsecureAllowBERIntegers allows credentials which have BER integer encoding for their signatures
// which do not conform to the specification. This is an experimental option that may be removed without any notice
// and could potentially lead to zero-day exploits due to the ambiguity of encoding practices. This is not a recommended
// option.
func SetExperimentalInsecureAllowBERIntegers(value bool) {
allowBERIntegers.Store(value)
}
@@ -0,0 +1,492 @@
package webauthncose
import (
"crypto"
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"hash"
"math"
"math/big"
"github.com/go-webauthn/x/encoding/asn1"
"github.com/google/go-tpm/tpm2"
"github.com/go-webauthn/webauthn/protocol/webauthncbor"
)
// PublicKeyData The public key portion of a Relying Party-specific credential key pair, generated
// by an authenticator and returned to a Relying Party at registration time. We unpack this object
// using fxamacker's cbor library ("github.com/fxamacker/cbor/v2") which is why there are cbor tags
// included. The tag field values correspond to the IANA COSE keys that give their respective
// values.
//
// Specification: §6.4.1.1. Examples of credentialPublicKey Values Encoded in COSE_Key Format (https://www.w3.org/TR/webauthn/#sctn-encoded-credPubKey-examples)
type PublicKeyData struct {
// Decode the results to int by default.
_struct bool `cbor:",keyasint" json:"public_key"` //nolint:govet,staticcheck
// The type of key created. Should be OKP, EC2, or RSA.
KeyType int64 `cbor:"1,keyasint" json:"kty"`
// A COSEAlgorithmIdentifier for the algorithm used to derive the key signature.
Algorithm int64 `cbor:"3,keyasint" json:"alg"`
}
type EC2PublicKeyData struct {
PublicKeyData
// If the key type is EC2, the curve on which we derive the signature from.
Curve int64 `cbor:"-1,keyasint,omitempty" json:"crv"`
// A byte string 32 bytes in length that holds the x coordinate of the key.
XCoord []byte `cbor:"-2,keyasint,omitempty" json:"x"`
// A byte string 32 bytes in length that holds the y coordinate of the key.
YCoord []byte `cbor:"-3,keyasint,omitempty" json:"y"`
}
type RSAPublicKeyData struct {
PublicKeyData
// Represents the modulus parameter for the RSA algorithm.
Modulus []byte `cbor:"-1,keyasint,omitempty" json:"n"`
// Represents the exponent parameter for the RSA algorithm.
Exponent []byte `cbor:"-2,keyasint,omitempty" json:"e"`
}
type OKPPublicKeyData struct {
PublicKeyData
Curve int64
// A byte string that holds the x coordinate of the key.
XCoord []byte `cbor:"-2,keyasint,omitempty" json:"x"`
}
// Verify Octet Key Pair (OKP) Public Key Signature.
func (k *OKPPublicKeyData) Verify(data []byte, sig []byte) (bool, error) {
if err := validateOKPPublicKey(k); err != nil {
return false, err
}
var key ed25519.PublicKey = make([]byte, ed25519.PublicKeySize)
copy(key, k.XCoord)
return ed25519.Verify(key, data, sig), nil
}
// Verify Elliptic Curve Public Key Signature.
func (k *EC2PublicKeyData) Verify(data []byte, sig []byte) (valid bool, err error) {
if err = validateEC2PublicKey(k); err != nil {
return false, err
}
pubkey := &ecdsa.PublicKey{
Curve: ec2AlgCurve(k.Algorithm),
X: big.NewInt(0).SetBytes(k.XCoord),
Y: big.NewInt(0).SetBytes(k.YCoord),
}
h := HasherFromCOSEAlg(COSEAlgorithmIdentifier(k.Algorithm))
h.Write(data)
e := &ECDSASignature{}
var opts []asn1.UnmarshalOpt
if allowBERIntegers.Load() {
opts = append(opts, asn1.WithUnmarshalAllowBERIntegers(true))
}
if _, err = asn1.Unmarshal(sig, e, opts...); err != nil {
return false, ErrSigNotProvidedOrInvalid
}
return ecdsa.Verify(pubkey, h.Sum(nil), e.R, e.S), nil
}
// ToECDSA converts the EC2PublicKeyData to an ecdsa.PublicKey.
func (k *EC2PublicKeyData) ToECDSA() (key *ecdsa.PublicKey, err error) {
if err = validateEC2PublicKey(k); err != nil {
return nil, err
}
return &ecdsa.PublicKey{
Curve: ec2AlgCurve(k.Algorithm),
X: big.NewInt(0).SetBytes(k.XCoord),
Y: big.NewInt(0).SetBytes(k.YCoord),
}, nil
}
// Verify RSA Public Key Signature.
func (k *RSAPublicKeyData) Verify(data []byte, sig []byte) (valid bool, err error) {
if err = validateRSAPublicKey(k); err != nil {
return false, err
}
e, _ := parseRSAPublicKeyDataExponent(k)
pubkey := &rsa.PublicKey{
N: big.NewInt(0).SetBytes(k.Modulus),
E: e,
}
coseAlg := COSEAlgorithmIdentifier(k.Algorithm)
algDetail, ok := COSESignatureAlgorithmDetails[coseAlg]
if !ok {
return false, ErrUnsupportedAlgorithm
}
hash := algDetail.hash
h := hash.New()
h.Write(data)
switch coseAlg {
case AlgPS256, AlgPS384, AlgPS512:
err = rsa.VerifyPSS(pubkey, hash, h.Sum(nil), sig, nil)
return err == nil, err
case AlgRS1, AlgRS256, AlgRS384, AlgRS512:
err = rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sig)
return err == nil, err
default:
return false, ErrUnsupportedAlgorithm
}
}
// ParsePublicKey figures out what kind of COSE material was provided and create the data for the new key.
func ParsePublicKey(keyBytes []byte) (publicKey any, err error) {
pk := PublicKeyData{}
if err = webauthncbor.Unmarshal(keyBytes, &pk); err != nil {
return nil, ErrUnsupportedKey
}
switch COSEKeyType(pk.KeyType) {
case OctetKey:
var o OKPPublicKeyData
if err = webauthncbor.Unmarshal(keyBytes, &o); err != nil {
return nil, err
}
o.PublicKeyData = pk
if err = validateOKPPublicKey(&o); err != nil {
return nil, err
}
return o, nil
case EllipticKey:
var e EC2PublicKeyData
if err = webauthncbor.Unmarshal(keyBytes, &e); err != nil {
return nil, err
}
e.PublicKeyData = pk
if err = validateEC2PublicKey(&e); err != nil {
return nil, err
}
return e, nil
case RSAKey:
var r RSAPublicKeyData
if err = webauthncbor.Unmarshal(keyBytes, &r); err != nil {
return nil, err
}
r.PublicKeyData = pk
if err = validateRSAPublicKey(&r); err != nil {
return nil, err
}
return r, nil
default:
return nil, ErrUnsupportedKey
}
}
// ParseFIDOPublicKey is only used when the appID extension is configured by the assertion response.
func ParseFIDOPublicKey(keyBytes []byte) (data EC2PublicKeyData, err error) {
key, err := ecdh.P256().NewPublicKey(keyBytes)
if err != nil {
return data, fmt.Errorf("failed to parse FIDO public key: %w", err)
}
// Raw bytes for an uncompressed P-256 point: 0x04 || x(32) || y(32).
raw := key.Bytes()
return EC2PublicKeyData{
PublicKeyData: PublicKeyData{
KeyType: int64(EllipticKey),
Algorithm: int64(AlgES256),
},
Curve: int64(P256),
XCoord: raw[1 : 1+ecCoordSize],
YCoord: raw[1+ecCoordSize:],
}, nil
}
func VerifySignature(key any, data []byte, sig []byte) (bool, error) {
switch k := key.(type) {
case OKPPublicKeyData:
return k.Verify(data, sig)
case EC2PublicKeyData:
return k.Verify(data, sig)
case RSAPublicKeyData:
return k.Verify(data, sig)
default:
return false, ErrUnsupportedKey
}
}
func DisplayPublicKey(cpk []byte) string {
parsedKey, err := ParsePublicKey(cpk)
if err != nil {
return keyCannotDisplay
}
var data []byte
switch k := parsedKey.(type) {
case RSAPublicKeyData:
var e int
if e, err = parseRSAPublicKeyDataExponent(&k); err != nil {
return keyCannotDisplay
}
rKey := &rsa.PublicKey{
N: big.NewInt(0).SetBytes(k.Modulus),
E: e,
}
if data, err = x509.MarshalPKIXPublicKey(rKey); err != nil {
return keyCannotDisplay
}
case EC2PublicKeyData:
curve := ec2AlgCurve(k.Algorithm)
if curve == nil {
return keyCannotDisplay
}
eKey := &ecdsa.PublicKey{
Curve: curve,
X: big.NewInt(0).SetBytes(k.XCoord),
Y: big.NewInt(0).SetBytes(k.YCoord),
}
if data, err = x509.MarshalPKIXPublicKey(eKey); err != nil {
return keyCannotDisplay
}
case OKPPublicKeyData:
if len(k.XCoord) != ed25519.PublicKeySize {
return keyCannotDisplay
}
var oKey ed25519.PublicKey = make([]byte, ed25519.PublicKeySize)
copy(oKey, k.XCoord)
if data, err = marshalEd25519PublicKey(oKey); err != nil {
return keyCannotDisplay
}
default:
return "Cannot display key of this type"
}
pemBytes := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: data,
})
return string(pemBytes)
}
func (k *EC2PublicKeyData) TPMCurveID() tpm2.TPMECCCurve {
switch COSEEllipticCurve(k.Curve) {
case P256:
return tpm2.TPMECCNistP256 // TPM_ECC_NIST_P256.
case P384:
return tpm2.TPMECCNistP384 // TPM_ECC_NIST_P384.
case P521:
return tpm2.TPMECCNistP521 // TPM_ECC_NIST_P521.
default:
return tpm2.TPMECCNone // TPM_ECC_NONE.
}
}
func ec2AlgCurve(coseAlg int64) elliptic.Curve {
switch COSEAlgorithmIdentifier(coseAlg) {
case AlgES512, AlgESP512:
return elliptic.P521()
case AlgES384, AlgESP384:
return elliptic.P384()
case AlgES256, AlgESP256:
return elliptic.P256()
default:
return nil
}
}
// SigAlgFromCOSEAlg return which signature algorithm is being used from the COSE Key.
func SigAlgFromCOSEAlg(coseAlg COSEAlgorithmIdentifier) x509.SignatureAlgorithm {
d, ok := COSESignatureAlgorithmDetails[coseAlg]
if !ok {
return x509.UnknownSignatureAlgorithm
}
return d.sigAlg
}
// HasherFromCOSEAlg returns the Hashing interface to be used for a given COSE Algorithm.
func HasherFromCOSEAlg(coseAlg COSEAlgorithmIdentifier) hash.Hash {
d, ok := COSESignatureAlgorithmDetails[coseAlg]
if !ok {
// default to SHA256? Why not.
return crypto.SHA256.New()
}
return d.hash.New()
}
var COSESignatureAlgorithmDetails = map[COSEAlgorithmIdentifier]struct {
name string
hash crypto.Hash
sigAlg x509.SignatureAlgorithm
}{
AlgRS1: {"SHA1-RSA", crypto.SHA1, x509.SHA1WithRSA},
AlgRS256: {"SHA256-RSA", crypto.SHA256, x509.SHA256WithRSA},
AlgRS384: {"SHA384-RSA", crypto.SHA384, x509.SHA384WithRSA},
AlgRS512: {"SHA512-RSA", crypto.SHA512, x509.SHA512WithRSA},
AlgPS256: {"SHA256-RSAPSS", crypto.SHA256, x509.SHA256WithRSAPSS},
AlgPS384: {"SHA384-RSAPSS", crypto.SHA384, x509.SHA384WithRSAPSS},
AlgPS512: {"SHA512-RSAPSS", crypto.SHA512, x509.SHA512WithRSAPSS},
AlgES256: {"ECDSA-SHA256", crypto.SHA256, x509.ECDSAWithSHA256},
AlgESP256: {"ECDSA-SHA256-Prehashed", crypto.SHA256, x509.ECDSAWithSHA256},
AlgES384: {"ECDSA-SHA384", crypto.SHA384, x509.ECDSAWithSHA384},
AlgESP384: {"ECDSA-SHA384-Prehashed", crypto.SHA384, x509.ECDSAWithSHA384},
AlgES512: {"ECDSA-SHA512", crypto.SHA512, x509.ECDSAWithSHA512},
AlgESP512: {"ECDSA-SHA512-Prehashed", crypto.SHA512, x509.ECDSAWithSHA512},
AlgEdDSA: {"EdDSA", crypto.SHA512, x509.PureEd25519},
AlgEd25519: {"Ed25519", crypto.SHA512, x509.PureEd25519},
}
type Error struct {
// Short name for the type of error that has occurred.
Type string `json:"type"`
// Additional details about the error.
Details string `json:"error"`
// Information to help debug the error.
DevInfo string `json:"debug"`
}
var (
ErrUnsupportedKey = &Error{
Type: "invalid_key_type",
Details: "Unsupported Public Key Type",
}
ErrUnsupportedAlgorithm = &Error{
Type: "unsupported_key_algorithm",
Details: "Unsupported public key algorithm",
}
ErrSigNotProvidedOrInvalid = &Error{
Type: "signature_not_provided_or_invalid",
Details: "Signature invalid or not provided",
}
)
func (err *Error) Error() string {
return err.Details
}
func (passedError *Error) WithDetails(details string) *Error {
err := *passedError
err.Details = details
return &err
}
func validateOKPPublicKey(k *OKPPublicKeyData) error {
if len(k.XCoord) != ed25519.PublicKeySize {
return ErrUnsupportedKey.WithDetails(fmt.Sprintf("OKP key x coordinate has invalid length %d, expected %d", len(k.XCoord), ed25519.PublicKeySize))
}
return nil
}
func validateEC2PublicKey(k *EC2PublicKeyData) error {
curve := ec2AlgCurve(k.Algorithm)
if curve == nil {
return ErrUnsupportedAlgorithm.WithDetails("Unsupported EC2 algorithm")
}
byteLen := (curve.Params().BitSize + 7) / 8
if len(k.XCoord) != byteLen || len(k.YCoord) != byteLen {
return ErrUnsupportedKey.WithDetails("EC2 key x or y coordinate has invalid length")
}
x := new(big.Int).SetBytes(k.XCoord)
y := new(big.Int).SetBytes(k.YCoord)
if !curve.IsOnCurve(x, y) {
return ErrUnsupportedKey.WithDetails("EC2 key point is not on curve")
}
return nil
}
func validateRSAPublicKey(k *RSAPublicKeyData) error {
n := new(big.Int).SetBytes(k.Modulus)
if n.Sign() <= 0 {
return ErrUnsupportedKey.WithDetails("RSA key contains zero or empty modulus")
}
if _, err := parseRSAPublicKeyDataExponent(k); err != nil {
return ErrUnsupportedKey.WithDetails(fmt.Sprintf("RSA key contains invalid exponent: %v", err))
}
return nil
}
func parseRSAPublicKeyDataExponent(k *RSAPublicKeyData) (exp int, err error) {
if k == nil {
return 0, fmt.Errorf("invalid key")
}
if len(k.Exponent) == 0 {
return 0, fmt.Errorf("invalid exponent length")
}
for _, b := range k.Exponent {
if exp > (math.MaxInt >> 8) {
return 0, ErrUnsupportedKey
}
exp = (exp << 8) | int(b)
}
if exp <= 0 {
return 0, ErrUnsupportedKey
}
return exp, nil
}
@@ -0,0 +1,680 @@
package webauthncose
import (
"crypto/ecdh"
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/google/go-tpm/tpm2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/go-webauthn/webauthn/protocol/webauthncbor"
)
// TestOKPSignatureVerification is a compatibility test to ensure that removing
// a previously used dependency doesn't introduce new issues.
//
// Since OKPs are used to represent Ed25519 keys, this test largely ensures
// that the underlying Ed25519 signature verification passes.
func TestOKPSignatureVerification(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)
data := []byte("Sample data to sign")
validSig := ed25519.Sign(priv, data)
invalidSig := []byte("invalid")
key := OKPPublicKeyData{
XCoord: pub,
}
// Test that a valid signature passes.
ok, err := key.Verify(data, validSig)
assert.True(t, ok)
assert.NoError(t, err)
ok, err = key.Verify(data, invalidSig)
assert.NoError(t, err)
assert.False(t, ok)
}
func TestP256SignatureVerification(t *testing.T) {
// Private/public key pair was generated with the following:
//
// $ openssl ecparam -genkey -name secp256r1 -noout -out private_key.pem
// $ openssl ec -in private_key.pem -noout -text
// Private-Key: (256 bit)
// priv:
// 48:7f:36:1d:df:d7:34:40:e7:07:f4:da:a6:77:5b:
// 37:68:59:e8:a3:c9:f2:9b:3b:b6:94:a1:29:27:c0:
// 21:3c
// pub:
// 04:f7:39:f8:c7:7b:32:f4:d5:f1:32:65:86:1f:eb:
// d7:6e:7a:9c:61:a1:14:0d:29:6b:8c:16:30:25:08:
// 87:03:16:c2:49:70:ad:78:11:cc:d9:da:7f:1b:88:
// f2:02:be:ba:c7:70:66:3e:f5:8b:a6:83:46:18:6d:
// d7:78:20:0d:d4
// ASN1 OID: prime256v1
// NIST CURVE: P-256
// ----.
pubX, err := hex.DecodeString("f739f8c77b32f4d5f13265861febd76e7a9c61a1140d296b8c16302508870316")
assert.NoError(t, err)
pubY, err := hex.DecodeString("c24970ad7811ccd9da7f1b88f202bebac770663ef58ba68346186dd778200dd4")
assert.NoError(t, err)
key := EC2PublicKeyData{
// These constants are from https://datatracker.ietf.org/doc/rfc9053/
// (see "ECDSA" and "Elliptic Curve Keys").
PublicKeyData: PublicKeyData{
KeyType: 2, // EC.
Algorithm: -7, // "ES256".
},
Curve: 1, // P-256.
XCoord: pubX,
YCoord: pubY,
}
data := []byte("webauthnFTW")
validSig, err := hex.DecodeString("3045022053584980793ee4ec01d583f303604c4f85a7e87df3fe9551962c5ab69a5ce27b022100c801fd6186ca4681e87fbbb97c5cb659f039473995a75a9a9dffea2708d6f8fb")
assert.NoError(t, err)
ok, err := VerifySignature(key, data, validSig)
assert.True(t, ok)
assert.NoError(t, err)
ok, err = VerifySignature(key, []byte("webauthnFTL"), validSig)
assert.NoError(t, err)
assert.False(t, ok)
}
func TestOKPDisplayPublicKey(t *testing.T) {
// Sample public key generated from ed25519.GenerateKey(rand.Reader).
var pub ed25519.PublicKey = []byte{0x7b, 0x88, 0x10, 0x24, 0xad, 0xc9, 0x82, 0xd3, 0x80, 0xb8, 0x77, 0x1e, 0x3b, 0x9b, 0xf8, 0xe4, 0xb3, 0x99, 0x8b, 0xc7, 0xd0, 0x58, 0x30, 0x66, 0x2, 0xce, 0x4d, 0xf, 0x2f, 0xe4, 0xb7, 0x81}
// The PEM encoded representation of the public key in PKIX, ASN.1 DER format.
expected := `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAe4gQJK3JgtOAuHceO5v45LOZi8fQWDBmAs5NDy/kt4E=
-----END PUBLIC KEY-----
`
key := OKPPublicKeyData{
XCoord: pub,
PublicKeyData: PublicKeyData{
KeyType: int64(OctetKey),
},
}
// Get the CBOR-encoded representation of the OKPPublicKeyData.
buf, _ := webauthncbor.Marshal(key)
actual := DisplayPublicKey(buf)
assert.Equal(t, expected, actual)
}
func TestRSAExponent(t *testing.T) {
testCases := []struct {
name string
have *RSAPublicKeyData
expected int
err string
}{
{
"ShouldHandle3ByteExponent",
&RSAPublicKeyData{
Exponent: []byte{0x01, 0x00, 0x01},
},
65537,
"",
},
{
"ShouldHandle3ByteExponentAlt",
&RSAPublicKeyData{
Exponent: []byte{0x01, 0x00, 0x02},
},
65538,
"",
},
{
"ShouldHandle3ByteExponentLarge",
&RSAPublicKeyData{
Exponent: []byte{0xff, 0xff, 0xff},
},
16777215,
"",
},
{
"ShouldHandle4ByteExponent",
&RSAPublicKeyData{
Exponent: []byte{0x01, 0x00, 0x02, 0xff},
},
16777983,
"",
},
{
"ShouldHandleZeroLength",
&RSAPublicKeyData{
Exponent: []byte{},
},
0,
"invalid exponent length",
},
{
"ShouldHandleNilExponent",
&RSAPublicKeyData{
Exponent: nil,
},
0,
"invalid exponent length",
},
{
"ShouldHandleNilKey",
nil,
0,
"invalid key",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual, err := parseRSAPublicKeyDataExponent(tc.have)
if tc.err != "" {
assert.EqualError(t, err, tc.err)
assert.Equal(t, 0, actual)
} else {
assert.Equal(t, tc.expected, actual)
assert.NoError(t, err)
}
})
}
}
func TestUnsupportedKeyMethods(t *testing.T) {
verified, err := VerifySignature(PublicKeyData{}, nil, nil)
assert.EqualError(t, err, "Unsupported Public Key Type")
assert.False(t, verified)
}
func TestParsePublicKey(t *testing.T) {
testCases := []struct {
name string
attestationObject string
clientDataJSON string
authenticatorData string
signature string
expected any
tpmcurve tpm2.TPMECCCurve
isEC2Key bool
isRSAKey bool
isOKPKey bool
}{
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-none-es256
"ShouldHandleTestVector17",
"a363666d74646e6f6e656761747453746d74a068617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b559000000008446ccb9ab1db374750b2367ff6f3a1f0020f91f391db4c9b2fde0ea70189cba3fb63f579ba6122b33ad94ff3ec330084be4a5010203262001215820afefa16f97ca9b2d23eb86ccb64098d20db90856062eb249c33a9b672f26df61225820930a56b87a2fca66334b03458abf879717c12cc68ed73290af2e2664796b9220",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224f63446e55685158756c5455506f334a5558543049393770767a7a59425039745a63685879617630314167222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b51900000000",
"3046022100f50a4e2e4409249c4a853ba361282f09841df4dd4547a13a87780218deffcd380221008480ac0f0b93538174f575bf11a1dd5d78c6e486013f937295ea13653e331e87",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0xaf, 0xef, 0xa1, 0x6f, 0x97, 0xca, 0x9b, 0x2d, 0x23, 0xeb, 0x86, 0xcc, 0xb6, 0x40, 0x98, 0xd2, 0xd, 0xb9, 0x8, 0x56, 0x6, 0x2e, 0xb2, 0x49, 0xc3, 0x3a, 0x9b, 0x67, 0x2f, 0x26, 0xdf, 0x61}, YCoord: []uint8{0x93, 0xa, 0x56, 0xb8, 0x7a, 0x2f, 0xca, 0x66, 0x33, 0x4b, 0x3, 0x45, 0x8a, 0xbf, 0x87, 0x97, 0x17, 0xc1, 0x2c, 0xc6, 0x8e, 0xd7, 0x32, 0x90, 0xaf, 0x2e, 0x26, 0x64, 0x79, 0x6b, 0x92, 0x20}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-self-es256
"ShouldHandleTestVector19",
"a363666d74667061636b65646761747453746d74a263616c67266373696758483046022100ae045923ded832b844cae4d5fc864277c0dc114ad713e271af0f0d371bd3ac540221009077a088ed51a673951ad3ba2673d5029bab65b64f4ea67b234321f86fcfac5d68617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b55d00000000df850e09db6afbdfab51697791506cfc0020455ef34e2043a87db3d4afeb39bbcb6cc32df9347c789a865ecdca129cbef58ca5010203262001215820eb151c8176b225cc651559fecf07af450fd85802046656b34c18f6cf193843c5225820927b8aa427a2be1b8834d233a2d34f61f13bfd44119c325d5896e183fee484f2",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a225248696843784e534e493352594d45314f7731476d3132786e726b634a5f6666707637546e2d4a71386773222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73652c22657874726144617461223a22636c69656e74446174614a534f4e206d617920626520657874656e6465642077697468206164646974696f6e616c206669656c647320696e20746865206675747572652c207375636820617320746869733a206754623533727a36456853576f6d58477a696d4331513d3d227d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50900000000",
"3044022076691be76a8618976d9803c4cdc9b97d34a7af37e3bdc894a2bf54f040ffae850220448033a015296ffb09a762efd0d719a55346941e17e91ebf64c60d439d0b9744",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0xeb, 0x15, 0x1c, 0x81, 0x76, 0xb2, 0x25, 0xcc, 0x65, 0x15, 0x59, 0xfe, 0xcf, 0x7, 0xaf, 0x45, 0xf, 0xd8, 0x58, 0x2, 0x4, 0x66, 0x56, 0xb3, 0x4c, 0x18, 0xf6, 0xcf, 0x19, 0x38, 0x43, 0xc5}, YCoord: []uint8{0x92, 0x7b, 0x8a, 0xa4, 0x27, 0xa2, 0xbe, 0x1b, 0x88, 0x34, 0xd2, 0x33, 0xa2, 0xd3, 0x4f, 0x61, 0xf1, 0x3b, 0xfd, 0x44, 0x11, 0x9c, 0x32, 0x5d, 0x58, 0x96, 0xe1, 0x83, 0xfe, 0xe4, 0x84, 0xf2}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-none-es256-crossOrigin
"ShouldHandleTestVector21",
"a363666d74646e6f6e656761747453746d74a068617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54500000000883f4f6014f19c09d87aa38123be48d000206e1050c0d2ca2f07c755cb2c66a74c64fa43065c18f938354d9915db2bd5ce57a501020326200121582022200a473f90b11078851550d03b4e44a2279f8c4eca27b3153dedfe03e4e97d225820cbd0be95e746ad6f5a8191be11756e4c0420e72f65b466d39bc56b8b123a9c6e",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a226832716c463771445f65356c5f505f62796b7945377135645650674547685f49584a6b655737736e4d5463222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a747275652c22657874726144617461223a22636c69656e74446174614a534f4e206d617920626520657874656e6465642077697468206164646974696f6e616c206669656c647320696e20746865206675747572652c207375636820617320746869733a2039327063545644304162792d713464746d6a366566673d3d227d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50500000000",
"304402204396b14b216ed47920dc359e46aa0a1d4a912cf9d50f25a58ec236a11db4cf5e02204fdb59ff01656c4b0868e415436a464b0e30e94b02c719b995afaba9c917146b",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x22, 0x20, 0xa, 0x47, 0x3f, 0x90, 0xb1, 0x10, 0x78, 0x85, 0x15, 0x50, 0xd0, 0x3b, 0x4e, 0x44, 0xa2, 0x27, 0x9f, 0x8c, 0x4e, 0xca, 0x27, 0xb3, 0x15, 0x3d, 0xed, 0xfe, 0x3, 0xe4, 0xe9, 0x7d}, YCoord: []uint8{0xcb, 0xd0, 0xbe, 0x95, 0xe7, 0x46, 0xad, 0x6f, 0x5a, 0x81, 0x91, 0xbe, 0x11, 0x75, 0x6e, 0x4c, 0x4, 0x20, 0xe7, 0x2f, 0x65, 0xb4, 0x66, 0xd3, 0x9b, 0xc5, 0x6b, 0x8b, 0x12, 0x3a, 0x9c, 0x6e}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-none-es256-topOrigin
"ShouldHandleTestVector23",
"a363666d74646e6f6e656761747453746d74a068617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b5410000000097586fd09799a76401c200455099ef2a0020b8ad59b996047ab18e2ceb57206c362da57458793481f4a8ebf101c7ca7cc0f1a5010203262001215820a1c47c1d82da4ebe82cd72207102b380670701993bc35398ae2e5726427fe01d22582086c1080d82987028c7f54ecb1b01185de243b359294a0ed210cd47480f0adc88",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22315570636a4b53324b6f34377379486a7372787a68572d466f51465132796b3572426c584f6573656f4759222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a747275652c22746f704f726967696e223a2268747470733a2f2f6578616d706c652e636f6d222c22657874726144617461223a22636c69656e74446174614a534f4e206d617920626520657874656e6465642077697468206164646974696f6e616c206669656c647320696e20746865206675747572652c207375636820617320746869733a205569466f4a4d565251484441465746693476785570513d3d227d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50500000000",
"304402206a19613fa8cfacfc8027272aec5dae3555fea9f983d841581466678d71e6761a02207a9785ba22e48eb18525850357d9dc70795aaad2e6021159c4a4a183146eaa71",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0xa1, 0xc4, 0x7c, 0x1d, 0x82, 0xda, 0x4e, 0xbe, 0x82, 0xcd, 0x72, 0x20, 0x71, 0x2, 0xb3, 0x80, 0x67, 0x7, 0x1, 0x99, 0x3b, 0xc3, 0x53, 0x98, 0xae, 0x2e, 0x57, 0x26, 0x42, 0x7f, 0xe0, 0x1d}, YCoord: []uint8{0x86, 0xc1, 0x8, 0xd, 0x82, 0x98, 0x70, 0x28, 0xc7, 0xf5, 0x4e, 0xcb, 0x1b, 0x1, 0x18, 0x5d, 0xe2, 0x43, 0xb3, 0x59, 0x29, 0x4a, 0xe, 0xd2, 0x10, 0xcd, 0x47, 0x48, 0xf, 0xa, 0xdc, 0x88}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-none-es256-long-credential-id
"ShouldHandleTestVector25",
"a363666d74646e6f6e656761747453746d74a0686175746844617461590483bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b549000000008f3360c2cd1b0ac14ffe0795c5d2638e03ff3a761a4e1674ad6c4305869435c0eee9c286172c229bb91b48b4ada140c0863417031305cce5b4a27a88d7fe728a5f5a627de771b4b40e77f187980c124f9fe832d7136010436a056cce716680587d23187cf1fc2c62ae86fc3e508ee9617ffc74fbc10488ec16ec5e9096328669a898709b655e549738c666c1ae6281dc3b5f733c251d3eefb76ee70a3805ca91bcc18e49c8dc7f63ebcb486ba8c3d6ab52b88ff72c6a5bb47c32f3ee8683a3ddc8abf60870448ec8a21b5bdcb183c7dead870255575a6df96eb1b6a2a1019780cba9e4887b17ff1164bbbcc10eb0d86ed75984cd3fa3419103024507dfd9ce8f92c56af7914cb0bb50b87ba82a312bb7dcd93028dbdcd6adb266979667158335171e3682d37755701edbf9d872846a291d49e57ef09da1ec637f5052ed2aa7407f7e61827468e94b461844f4c67be5fa9c6055a566f8fdfc29d4bf78a9ff275f552cc68ba543fa3962eea36fd1ea8453764577d021d0a181efc1f6100ab2e4110039e21ee16970bda7432b6134492155afc126295b3a2eccd12c66a68e340969e995e3e8c9c476e395cfc21203414110779474f1c9797406637dbe414f132519d3bf0ce4f01734ef0e1a12c3ad604ff15d766b1624db6a5a7ccbff7bc35c9908df94aba277e0af48f04ff3d16381c47e5a37ed3988a67a3b1ecaa926336b33391fff04128f869991c9fabd905b6fe3ceef5f8b630ec1c5d2636d5b1961ad5ca5004170f6f5e482792aad989b0287fe91e5c479403397152f1fa56aa79b156eb47e6c8ea3eb175c34cfb38ad8e772874639b1023d4d01395c94e55831671cc022aa6fa1e02a02c2e4abc776f6960e51f83b71a8c0f207b6a347573977812c9aa5480b0011aa739bd4b76c18c000cc4757cceccb920f007c40c00e37e5ab21476cd9f6054a8fffb55a108f5c706e2cea2049d81fd321ff47d2a5761b0800955ab1d4f4889f55a84e2601c684f17a4ade7453ea49591d0b59c8d9a765052f62219cf6ef4a5dd9539f0617d6ebbebce7c000455475d18449e25c49ef9a1e3efe18c09082ebe2058d7c347defaa92f0664553b805c7d76bbfce5f330aca220ac90a789380fc479ea0d8793205813cca590a912f699ad52f991a1bc0a503c3ec4b2a696719e3c26591a87127f7305cc7e72f4c8e39355ebb06a5b1042990f38710ee7aa612ee4374bb82e878585a70a96c2a6b47f101a4ff154be4fd76a3167577a5cc54d9167c154c69ac35485e44cc898b719e1be3cc9c0fb5624b8f8a0dae10947a41bf848b6c1bb33d1006ec077d7e286e3f2a7b4843716390119449fe2721e81a5ed2333d331c7120765da58fadae73c19d9a8c4509cf8ac1e9d98b799a5274509069739b5823f3fb496663820033426988eefca53e580e0f9e0dfe0992fc2e53a97e053639f98577058f995bdbd41cefdba50102032620012158203b8176b7504489cc593046d7988abb7905a742de6ac2cdc748a873c663e90cb12258201436d5edc9a75f23999eef9d5950a5c2455514ee1014084720f841a06b828a11",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22377833727057334f53505a307045664d396a75566d53574d36485a4935634f573875384d6f647047446a73222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50d00000000",
"304502203ecef83fb12a0cae7841055f9f87103a99fd14b424194bbf06c4623d3ee6e3fd022100d2ace346db262b1374a6b70faa51f518a42ddca13a4125ce6f5052a75bac9fb6",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x3b, 0x81, 0x76, 0xb7, 0x50, 0x44, 0x89, 0xcc, 0x59, 0x30, 0x46, 0xd7, 0x98, 0x8a, 0xbb, 0x79, 0x5, 0xa7, 0x42, 0xde, 0x6a, 0xc2, 0xcd, 0xc7, 0x48, 0xa8, 0x73, 0xc6, 0x63, 0xe9, 0xc, 0xb1}, YCoord: []uint8{0x14, 0x36, 0xd5, 0xed, 0xc9, 0xa7, 0x5f, 0x23, 0x99, 0x9e, 0xef, 0x9d, 0x59, 0x50, 0xa5, 0xc2, 0x45, 0x55, 0x14, 0xee, 0x10, 0x14, 0x8, 0x47, 0x20, 0xf8, 0x41, 0xa0, 0x6b, 0x82, 0x8a, 0x11}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-es256
"ShouldHandleTestVector27",
"a363666d74667061636b65646761747453746d74a363616c67266373696758473045022025fcee945801b94e63d7c029e6f761654cf02e7100d5364a3b90e03daa6276fc022100eabcdf4ce19feb0980e829c3b6137079b18e42f43ce5c3c573b83368794f354c637835638159022530820221308201c8a00302010202110088c220f83c8ef1feafe94deae45faad0300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d03010703420004a91ba4389409dd38a428141940ca8feb1ac0d7b4350558104a3777a49322f3798440f378b3398ab2d3bb7bf91322c92eb23556f59ad0a836fec4c7663b0e4dc3a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414a589ba72d060842ab11f74fb246bdedab16f9b9b301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e300a06082a8648ce3d040302034700304402201726b9d85ecd8a5ed51163722ca3a20886fd9b242a0aa0453d442116075defd502207ef471e530ac87961a88a7f0d0c17b091ffc6b9238d30f79f635b417be5910e768617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54d00000000876ca4f52071c3e9b25509ef2cdf7ed60020c9a6f5b3462d02873fea0c56862234f99f081728084e511bb7760201a89054a5a50102032620012158201cf27f25da591208a4239c2e324f104f585525479a29edeedd830f48e77aeae522582059e4b7da6c0106e206ce390c93ab98a15a5ec3887e57f0cc2bece803b920c423",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273524276704770587676463446524841565833496d4b4130453958773858306b526a44426c4d6668726255222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73652c22657874726144617461223a22636c69656e74446174614a534f4e206d617920626520657874656e6465642077697468206164646974696f6e616c206669656c647320696e20746865206675747572652c207375636820617320746869733a20415a4d77794d78496244382d756775464e70367238513d3d227d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50d00000000",
"30460221009d8d54895393894d37b9fa7bdfbcff05403de3cf0d6443ffb394fa239f101579022100c8871288f19c6c48a3b64c09d39868c12d16ed80ea4c5d8890288975c0272f50",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x1c, 0xf2, 0x7f, 0x25, 0xda, 0x59, 0x12, 0x8, 0xa4, 0x23, 0x9c, 0x2e, 0x32, 0x4f, 0x10, 0x4f, 0x58, 0x55, 0x25, 0x47, 0x9a, 0x29, 0xed, 0xee, 0xdd, 0x83, 0xf, 0x48, 0xe7, 0x7a, 0xea, 0xe5}, YCoord: []uint8{0x59, 0xe4, 0xb7, 0xda, 0x6c, 0x1, 0x6, 0xe2, 0x6, 0xce, 0x39, 0xc, 0x93, 0xab, 0x98, 0xa1, 0x5a, 0x5e, 0xc3, 0x88, 0x7e, 0x57, 0xf0, 0xcc, 0x2b, 0xec, 0xe8, 0x3, 0xb9, 0x20, 0xc4, 0x23}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-es384
"ShouldHandleTestVector29",
"a363666d74667061636b65646761747453746d74a363616c67266373696758473045022100c56ecc970b7843833e0f461fde26233f61eb395161d481558c08b9c6ed61675b022029f5e05033705cd0f9b0a07e149468ec308a4f84906409efdceb1da20a7518d6637835638159022530820221308201c7a00302010202103d0a5588bb87ebb1d4cee4a1807c1b7c300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d0301070342000417e5cc91d676d370e36aa7de40c25aacb45a3845f13d2932088ece2270b9b431241c219c22d0c256c9438ade00f2c05e62f8ef906b9b997ae9f3c460c2db66f5a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414c7c8dd95382a2230e4c0dd3664338fa908169a9c301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e300a06082a8648ce3d0403020348003045022054068cc9ae038937b7c468c307edb9c6927ffdeb6a20070c483eb40330f99f10022100cf41953919c3c04693d6b1f42a613753f204e70e85fc6e9b17036170b83596e068617574684461746158c5bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b55900000000e950dcda3bdae1d087cda380a897848b0020953ae2dd9f28b1a1d5802c83e1f65833bb9769a08de82d812bc27c13fc6f06a9a5010203382220022158304866bd8b01da789e9eb806e5eab05ae5a638542296ab057a2f1bbce9b58f8a08b9171390b58a37ac7fffc2c5f45857da2258302a0b024c7f4b72072a1f96bd30a7261aae9571dd39870eb29e55c0941c6b08e89629a1ea1216aa64ce57c2807bf3901a",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a225f304844306c32396957623759654b4f39655277516545333753614649454574646941726f4b307446464d222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50d00000000",
"3065023100e4efbb46745ed00e67c4d51ab2bacab2af62ffa8b7c5fecec6d7d9bf2582275034a713a3dd731685eee81adfaf6aa63f0230161655353f07e018a3c2539f8de7c8c4cf88d4c32d2be29fe4e76fa096ecc9458bbfe0895d57129ab324130e6f0692db",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -35}, Curve: 2, XCoord: []uint8{0x48, 0x66, 0xbd, 0x8b, 0x1, 0xda, 0x78, 0x9e, 0x9e, 0xb8, 0x6, 0xe5, 0xea, 0xb0, 0x5a, 0xe5, 0xa6, 0x38, 0x54, 0x22, 0x96, 0xab, 0x5, 0x7a, 0x2f, 0x1b, 0xbc, 0xe9, 0xb5, 0x8f, 0x8a, 0x8, 0xb9, 0x17, 0x13, 0x90, 0xb5, 0x8a, 0x37, 0xac, 0x7f, 0xff, 0xc2, 0xc5, 0xf4, 0x58, 0x57, 0xda}, YCoord: []uint8{0x2a, 0xb, 0x2, 0x4c, 0x7f, 0x4b, 0x72, 0x7, 0x2a, 0x1f, 0x96, 0xbd, 0x30, 0xa7, 0x26, 0x1a, 0xae, 0x95, 0x71, 0xdd, 0x39, 0x87, 0xe, 0xb2, 0x9e, 0x55, 0xc0, 0x94, 0x1c, 0x6b, 0x8, 0xe8, 0x96, 0x29, 0xa1, 0xea, 0x12, 0x16, 0xaa, 0x64, 0xce, 0x57, 0xc2, 0x80, 0x7b, 0xf3, 0x90, 0x1a}},
tpm2.TPMECCNistP384,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-es512
"ShouldHandleTestVector31",
"a363666d74667061636b65646761747453746d74a363616c67266373696758483046022100c48fcbd826bbc79680802026688d41ab6da8c3a1d22ab6cecf36c8d7695d22500221008767dfe591277e973078d5692c8c35cf9d579792822e7145c96a0ac4515df5b0637835638159022730820223308201c8a0030201020211008a128b7ebe52b993835779e6d9b81355300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d03010703420004940b68885291536e2f7c60c05acfb252e7eebcf4304425dd93ab7b1962f20492bf18dc0f12862599e81fb764ac92151f9a78fcbb35d7a26c8c52949b18133c06a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e041604143ffad863abcd3dc5717b8a252189f41af97e7f31301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e300a06082a8648ce3d0403020349003046022100832c8b64c4f0188bd32e1bec63e13301cdc03165d3ef840d1f3dabb9a5719f83022100add57a9d5bedec98f29222dfc97ea795d055ee13a02a153d02be9ce00aedeb9168617574684461746158e9bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54d0000000039d8ce6a3cf61025775083a738e5c2540020d17d5af7e3f37c56622a67c8462c9e1c6336dfccb8b61d359dc47378dba58ce4a5010203382320032158420083240a2c3ad21a3dc0a6daa3d8bc05a46d7cd9825ba010ae2a22686c2d6d663d7d5f678987fb1e767542e63dc197ae915e25f8ee284651af29066910a2cc083f50225842017337df47ab5cce5d716ef8caffa97a3012689b1f326ea6c43a1ba9596c72f71f0122390143552b42be772b4c35ffb961220c743b486a601ea4cb6d5412f5b078d3",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22434e4d5a4447334c5055384d746c6d674d7a763136684a4e337a61677a5450564945734e65694b6f7a436279355046703067416f5848657a2d794c67386366306d6f66557669306c3653313565416a6471716d31635637394f6d72616b7a6e544253706f666278644c3479484777525234476b66563630546855473374793536714a4d334b6577635a6b7679354e376134574674434f7a7671416f71553745445a6a7a6c7149454569436b222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b51900000000",
"3081870242009bda02fe384e77bcb9fb42b07c395b7a53ec9d9616dd0308ab8495c2141c8364c7d16e212a4a4fb8e3987ff6c99eafd64d8484fd28c3fc7968f658a9033d1bb1b802416383e9f3ee20c691b66620299fef36bea2df4d39c92b2ead92f58e7b79ab0d9864d2ebf3b0dcc66ea13234492ccee6e9d421db43c959bcb94c162dc9494136c9f6",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -36}, Curve: 3, XCoord: []uint8{0x0, 0x83, 0x24, 0xa, 0x2c, 0x3a, 0xd2, 0x1a, 0x3d, 0xc0, 0xa6, 0xda, 0xa3, 0xd8, 0xbc, 0x5, 0xa4, 0x6d, 0x7c, 0xd9, 0x82, 0x5b, 0xa0, 0x10, 0xae, 0x2a, 0x22, 0x68, 0x6c, 0x2d, 0x6d, 0x66, 0x3d, 0x7d, 0x5f, 0x67, 0x89, 0x87, 0xfb, 0x1e, 0x76, 0x75, 0x42, 0xe6, 0x3d, 0xc1, 0x97, 0xae, 0x91, 0x5e, 0x25, 0xf8, 0xee, 0x28, 0x46, 0x51, 0xaf, 0x29, 0x6, 0x69, 0x10, 0xa2, 0xcc, 0x8, 0x3f, 0x50}, YCoord: []uint8{0x1, 0x73, 0x37, 0xdf, 0x47, 0xab, 0x5c, 0xce, 0x5d, 0x71, 0x6e, 0xf8, 0xca, 0xff, 0xa9, 0x7a, 0x30, 0x12, 0x68, 0x9b, 0x1f, 0x32, 0x6e, 0xa6, 0xc4, 0x3a, 0x1b, 0xa9, 0x59, 0x6c, 0x72, 0xf7, 0x1f, 0x1, 0x22, 0x39, 0x1, 0x43, 0x55, 0x2b, 0x42, 0xbe, 0x77, 0x2b, 0x4c, 0x35, 0xff, 0xb9, 0x61, 0x22, 0xc, 0x74, 0x3b, 0x48, 0x6a, 0x60, 0x1e, 0xa4, 0xcb, 0x6d, 0x54, 0x12, 0xf5, 0xb0, 0x78, 0xd3}},
tpm2.TPMECCNistP521,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-rs256
"ShouldHandleTestVector33",
"a363666d74667061636b65646761747453746d74a363616c672663736967584730450221008b8c5c6ea8c142c032e0be69e1353d44461c5c9109941cdda951b976eb95b6b302204d52f406c19e254b3ff9589bd18070fb055ac8db12fdd0a6734bea9d7168e900637835638159022630820222308201c7a00302010202101f6fb7a5ece81b45896b983a995da5f3300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d03010703420004b7b36b7542a11120b443c794d0c99fdc25a06b76586413d81e086163ef6fe147a557afc34e2861d9057d6d465d4705a0310550bdeeb5f35ee35b9425ab859981a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414fb37b647bccfb9e54d989eaaacc1633868703fb3301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e300a06082a8648ce3d0403020349003046022100b86bc129d92afca7d9869a39f70f139a305b4073a39eb654d81424bed5757d91022100cf9f7c60cab7c4a7d3e7f0020f281a93d4fd0a9f95121b989f56932a68885fba68617574684461746159021bbfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b55d00000000428f8878298b9862a36ad8c7527bfef20020992a18acc83f67533600c1138a4b4c4bd236de13629cf025ed17cb00b00b74dfa4010303390100205901b403fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012143010001",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224b56395a39667150356978617970346e596d7834794e6f33617562597a5333536d75757459423462784d55222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b51900000000",
"01063d52d7c39b4d432fc7063c5d93e582bdcb16889cd71f888d67d880ea730a428498d3bc8e1ee11f2b1ecbe6c292b118c55ffaaddefa8cad0a54dd137c51f1eec673f1bb6c4d1789d6826a222b22d0f585fc901fdc933212e579d199b89d672aa44891333e6a1355536025e82b25590256c3538229b55737083b2f6b9377e49e2472f11952f79fdd0da180b5ffd901b4049a8f081bb40711bef76c62aed943571f2d0575304cb549d68d8892f95086a30f93716aee818f8dc06e96c0d5e0ed4cfa9fd8773d90464b68cf140f7986666ff9c9e3302acd0535d60d769f465e2ab57ef8aabc89fccfef7ba32a64154a8b3d26be2298f470b8cc5377dbe3dfd4b0b45f8f01e63bde6cfc76b62771f9b70aa27cf40152cad93aa5acd784fd4b90f676e2ea828d0bf2400aebbaae4153e5838f537f88b6228346782a93a899be66ec77de45b3efcf311da6321c92e6b0cd11bfe653bf3e98cee8e341f02d67dbb6f9c98d9e8178090cfb5b70fbc6d541599ac794ae2f1d4de1286ec8de8c2daf7b1d15c8438e90d924df5c19045220a4c8438c1b979bbe016cf3d0eeec23c3999d4882cc645b776de930756612cdc6dd398160ff02a6",
RSAPublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 3, Algorithm: -257}, Modulus: []uint8{0x3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, Exponent: []uint8{0x1, 0x0, 0x1}},
tpm2.TPMECCNistP256,
false,
true,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-packed-ed25519
"ShouldHandleTestVector35",
"a363666d74667061636b65646761747453746d74a363616c672663736967584730450220730a54d4f76cb1f2b7dd4a5a6eee3374e3c8a60fb3c4daa527c9277e365b64aa0221008b31a04a28cc4148b14c42a916548ee7f430bc7629295b42ee93e5d1aaba8ee6637835638159022530820221308201c7a00302010202106e391f23f57150dc7a12dad18f2b43ad300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d03010703420004fb96a581a0b36742a8c45d6ffb5af1ef155524b50339445ec1109874045e0087db77edef91f3dc949927470d84b01627087b72c86b7c9d02e1389cba680ffc36a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414ef2ce1a86caba85121130a16e8ce82a75d5a6653301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e300a06082a8648ce3d0403020348003045022100ad4fa628cffba5a642a562cbfefe63efecce26d90c80114114d1745383e12f01022028af87ba3b0ff868a34b9458bc6973b27380a328dd87b7436651ffa823280bca6861757468446174615881bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54900000000164009ea09faae7c397bc3e2ad0e7ec00020c6cffa01b7fda368a7e0b29c1384a719820246bca894dd12914708743af0cecda401010327200621582089f81eba4a1f510cb243ff7fb9e9cf899bf627e49ce1ac3c3eae8adb2a8d7d7b",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224e35446169797479376f7a686c32463465744f4d763670706672504b41546f545170694856726d48686173222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b51900000000",
"4c873571377ac019f257d6bf07249f63ac2487483c51bc511ce0f0e3266c840cb07a09cdc445a2f963d8603a9f0f6cf9ce709d7fc6a96c7c51ea08d33776010c",
OKPPublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 1, Algorithm: -8}, Curve: 0, XCoord: []uint8{0x89, 0xf8, 0x1e, 0xba, 0x4a, 0x1f, 0x51, 0xc, 0xb2, 0x43, 0xff, 0x7f, 0xb9, 0xe9, 0xcf, 0x89, 0x9b, 0xf6, 0x27, 0xe4, 0x9c, 0xe1, 0xac, 0x3c, 0x3e, 0xae, 0x8a, 0xdb, 0x2a, 0x8d, 0x7d, 0x7b}},
tpm2.TPMECCNistP256,
false,
false,
true,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-tpm-es256
"ShouldHandleTestVector37",
"a363666d746374706d6761747453746d74a663616c67266373696758463044022066e5826a652091030fd444e33c3eca2bc6dc548cf3045013addb38aa6457a21002203f3a5c95c9e707d0e555041bcc8698ee4ebc04e26cc8bae459705471789851766376657263322e30637835638159023a30820236308201dca0030201020210311fc42da0ab10c43a9b1bf3a75e34e2300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004c54e3f109094f60d7699b7db5d838569ffd1f3e1c9e897cd9eb40063f9402e3e9937e936cf1fcd5eb743ff443c97ab2edcd7c8e0e6cf6cfd413b8ab19fffa769a381d33081d0300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e041604145f546cb6973d4981e80fcdc7463859f5879680e4301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e30100603551d250409300706056781050803305e0603551d110101ff04543052a450304e314c3014060567810502010c0b69643a30303030303030303014060567810502030c0b69643a3030303030303030301e060567810502020c15576562417574686e207465737420766563746f7273300a06082a8648ce3d0403020348003045022063c9a2797b8066f1db34dd609f1ab6695607e7a98e9ff8090a68853c9a9fc949022100a55831a39f5b8a2aa9a68837829cabf43fea2a5cea4859ae851cac78e6ac3e97677075624172656158560023000b0004000000000010001000030010002041202698c9d9753fb4bb3f27cd09fe6b8afdb76438ee2ae54d7c9dade10d864b0020d8735115cdb330a63ea1d6e43d5000f4bd56f99bce83ee1d73301fc270116d076863657274496e666f5869ff544347801700000020277d0e05579dd013215a62273f7f3a3e7e191ead2654a3036d75a5a3ee37a6b0000000000000000011111111222222223300000000000000000022000b9c42d8aad5939331b9af3711af179f17123178098c9a7d0ca89fcd1fc800f3c7000068617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54d000000004b92a377fc5f6107c4c85c190adbfd990020ec27bec7521c894bbb821105ea3724c90e770cf1fa354157ef18d0f18f78bea9a501020326200121582041202698c9d9753fb4bb3f27cd09fe6b8afdb76438ee2ae54d7c9dade10d864b225820d8735115cdb330a63ea1d6e43d5000f4bd56f99bce83ee1d73301fc270116d07",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2241416b375a7349645731364a393642776768474a422d6f2d554330304f7a464c6a46705531693279417673222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50d00000000",
"3045022060dc76b1607ec716c6e5eba8d056695ed6bc47b2e3d7a729c34e759e3ab66aa0022100d010a9e8fddcb64c439dfdca628ddb33cf245d567d157d9f66f942601bed9b38",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x41, 0x20, 0x26, 0x98, 0xc9, 0xd9, 0x75, 0x3f, 0xb4, 0xbb, 0x3f, 0x27, 0xcd, 0x9, 0xfe, 0x6b, 0x8a, 0xfd, 0xb7, 0x64, 0x38, 0xee, 0x2a, 0xe5, 0x4d, 0x7c, 0x9d, 0xad, 0xe1, 0xd, 0x86, 0x4b}, YCoord: []uint8{0xd8, 0x73, 0x51, 0x15, 0xcd, 0xb3, 0x30, 0xa6, 0x3e, 0xa1, 0xd6, 0xe4, 0x3d, 0x50, 0x0, 0xf4, 0xbd, 0x56, 0xf9, 0x9b, 0xce, 0x83, 0xee, 0x1d, 0x73, 0x30, 0x1f, 0xc2, 0x70, 0x11, 0x6d, 0x7}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-android-key-es256
"ShouldHandleTestVector39",
"a363666d746b616e64726f69642d6b65796761747453746d74a363616c672663736967584630440220592bbc3c4c5f6158b52be1e085c92848986d7844245dfc9512e1a7e9ff7a2cd8022015bdd0852d3bd091e1c22da4211f4ccf0fdf4d912599d1c6630b1f310d3166f5637835638159026d3082026930820210a00302010202101ff91f76b63f44812f998b250b0286bf300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d0301070342000499169657036d089a2a9821a7d0063d341f1a4613389359636efab5f3cbf1accfdd91c55543176ea99b644406dd1dd63774b6af65ac759e06ff40b1c8ab02df6ba381a83081a5300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e041604141ac81e50641e8d1339ab9f7eb25f0cd5aac054b0301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e3045060a2b06010401d679020111043730350202012c0201000201000201000420b20e943e3a7544b3a438943b6d5655313a47ef1af34e00ff3261aeb9ed155817040030003000300a06082a8648ce3d040302034700304402206f4609c9ffc946c418cef04c64a0d07bcce78f329b99270b822f2a4d1e3b75330220093c8d18328f36ef157f296393bdc7721dd2bd67438ffeaa42f051a044b7457168617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b55d00000000ade9705e1ce7085b899a540d02199bf800200a4729519788b6ed8a2d772b494e186244d8c798c052960dbc8c10c915176795a501020326200121582099169657036d089a2a9821a7d0063d341f1a4613389359636efab5f3cbf1accf225820dd91c55543176ea99b644406dd1dd63774b6af65ac759e06ff40b1c8ab02df6b",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22354f344679703238375851525a55447954746d74786971756851645742534b45545f702d36685433723459222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73652c22657874726144617461223a22636c69656e74446174614a534f4e206d617920626520657874656e6465642077697468206164646974696f6e616c206669656c647320696e20746865206675747572652c207375636820617320746869733a2071784a78422d5f78677277794d4c3631386472536e413d3d227d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50900000000",
"304502202060107d953b286aa1bf35e3e8c78b383fddab5591b2db17ffb23ed83fe7df20022100a99be0297cb0d9d38aa96f30b760a4e0749dab385acd2a51d0560caae570d225",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x99, 0x16, 0x96, 0x57, 0x3, 0x6d, 0x8, 0x9a, 0x2a, 0x98, 0x21, 0xa7, 0xd0, 0x6, 0x3d, 0x34, 0x1f, 0x1a, 0x46, 0x13, 0x38, 0x93, 0x59, 0x63, 0x6e, 0xfa, 0xb5, 0xf3, 0xcb, 0xf1, 0xac, 0xcf}, YCoord: []uint8{0xdd, 0x91, 0xc5, 0x55, 0x43, 0x17, 0x6e, 0xa9, 0x9b, 0x64, 0x44, 0x6, 0xdd, 0x1d, 0xd6, 0x37, 0x74, 0xb6, 0xaf, 0x65, 0xac, 0x75, 0x9e, 0x6, 0xff, 0x40, 0xb1, 0xc8, 0xab, 0x2, 0xdf, 0x6b}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
{
// Test Vector: https://www.w3.org/TR/webauthn-3/#sctn-test-vectors-apple-es256
"ShouldHandleTestVector41",
"a363666d74656170706c656761747453746d74a1637835638159025c30820258308201fea0030201020210394275613d5310b81a29ce90f48b61c1300a06082a8648ce3d0403023062311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331253023060355040b0c1c41757468656e74696361746f72204174746573746174696f6e204341310b30090603550406130241413020170d3234303130313030303030305a180f33303234303130313030303030305a305f311e301c06035504030c15576562417574686e207465737420766563746f7273310c300a060355040a0c0357334331223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e310b30090603550406130241413059301306072a8648ce3d020106082a8648ce3d030107034200048a3d5b1b4c543a706bf6e4b00afedb3c930b690dd286934fe2911f779cc7761af728e1aa3b0ff66692192daa776b83ddf8e3340d2d9a0eabdfc324eb3e2f136ca38196308193300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e0416041412f1ce6c0ae39b403bfc9200317bc183a4e4d766301f0603551d2304183016801445aff715b0dd786741fee996ebc16547a3931b1e303306092a864886f76364080204263024a122042097851a1a98b69c0614b26a94b70ec3aa07c061f89dbee23fbee01b6c42d718b0300a06082a8648ce3d040302034800304502207d541a5553f38b93b78b26a9dca58e64a7f8fac15ca206ae3ea32497cda375fb0221009137c6b75e767ec08224b29a7f703db4b745686dcc8a26b66e793688866d064f68617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b54900000000748210a20076616a733b2114336fc38400209c4a5886af9283d9be3e9ec55978dedfdce2e3b365cab193ae850c16238fafb8a50102032620012158208a3d5b1b4c543a706bf6e4b00afedb3c930b690dd286934fe2911f779cc7761a225820f728e1aa3b0ff66692192daa776b83ddf8e3340d2d9a0eabdfc324eb3e2f136c",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22302d73705a4751654a76375149304136637433676b37476353366b416a442d6432445f503030656d625155222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50900000000",
"3046022100ee35db795ce28044e1f8231d68b3d79a9882f7415aa35c1b5ac74d24251073c8022100dcc65691650a412d0ceef843710c09827acf26c7845bddac07eec95863e7fc4c",
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: 1, XCoord: []uint8{0x8a, 0x3d, 0x5b, 0x1b, 0x4c, 0x54, 0x3a, 0x70, 0x6b, 0xf6, 0xe4, 0xb0, 0xa, 0xfe, 0xdb, 0x3c, 0x93, 0xb, 0x69, 0xd, 0xd2, 0x86, 0x93, 0x4f, 0xe2, 0x91, 0x1f, 0x77, 0x9c, 0xc7, 0x76, 0x1a}, YCoord: []uint8{0xf7, 0x28, 0xe1, 0xaa, 0x3b, 0xf, 0xf6, 0x66, 0x92, 0x19, 0x2d, 0xaa, 0x77, 0x6b, 0x83, 0xdd, 0xf8, 0xe3, 0x34, 0xd, 0x2d, 0x9a, 0xe, 0xab, 0xdf, 0xc3, 0x24, 0xeb, 0x3e, 0x2f, 0x13, 0x6c}},
tpm2.TPMECCNistP256,
true,
false,
false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var (
keyBytes []byte
err error
)
keyBytes = MustExtractCBORKeyFromAttestationObject(t, tc.attestationObject)
result, err := ParsePublicKey(keyBytes)
assert.NoError(t, err)
assert.NotNil(t, result)
data := MustConstructSignedData(t, tc.clientDataJSON, tc.authenticatorData)
signature := MustDecodeHex(t, tc.signature)
switch key := result.(type) {
case EC2PublicKeyData:
assert.True(t, tc.isEC2Key)
assert.False(t, tc.isRSAKey)
assert.False(t, tc.isOKPKey)
assert.Equal(t, tc.tpmcurve, key.TPMCurveID())
ec, err := key.ToECDSA()
assert.NoError(t, err)
assert.NotNil(t, ec)
case RSAPublicKeyData:
assert.False(t, tc.isEC2Key)
assert.True(t, tc.isRSAKey)
assert.False(t, tc.isOKPKey)
case OKPPublicKeyData:
assert.False(t, tc.isEC2Key)
assert.False(t, tc.isRSAKey)
assert.True(t, tc.isOKPKey)
default:
t.Fatalf("Unexpected key type: %T", key)
}
ok, err := VerifySignature(result, data, signature)
assert.NoError(t, err)
assert.True(t, ok)
assert.Equal(t, tc.expected, result)
SetExperimentalInsecureAllowBERIntegers(true)
ok, err = VerifySignature(result, data, signature)
assert.NoError(t, err)
assert.True(t, ok)
assert.Equal(t, tc.expected, result)
SetExperimentalInsecureAllowBERIntegers(false)
display := DisplayPublicKey(keyBytes)
assert.NotEmpty(t, display)
assert.NotEqual(t, keyCannotDisplay, display)
})
}
}
func TestParsePublicKeyValidation(t *testing.T) {
mustMarshalCOSEKey := func(t *testing.T, kty, alg int64, extra map[int64]any) []byte {
t.Helper()
m := map[int64]any{1: kty, 3: alg}
for k, v := range extra {
m[k] = v
}
data, err := webauthncbor.Marshal(m)
require.NoError(t, err)
return data
}
// Generate valid key material for accept tests.
okpPub, _, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)
ec2Priv, err := ecdh.P256().GenerateKey(rand.Reader)
require.NoError(t, err)
ec2Pub := ec2Priv.PublicKey().Bytes()
ec2X := ec2Pub[1:33]
ec2Y := ec2Pub[33:65]
// COSE Key parameters per RFC 9052 §7 and RFC 9053 §2/§7:
// 1 (kty), 3 (alg), -1 (crv / n), -2 (x / e), -3 (y).
testCases := []struct {
name string
input []byte
err string
}{
{
"ShouldAcceptValidOKPKey",
mustMarshalCOSEKey(t, int64(OctetKey), int64(AlgEdDSA), map[int64]any{-2: []byte(okpPub)}),
"",
},
{
"ShouldAcceptValidEC2Key",
mustMarshalCOSEKey(t, int64(EllipticKey), int64(AlgES256), map[int64]any{-1: int64(P256), -2: ec2X, -3: ec2Y}),
"",
},
{
"ShouldAcceptValidRSAKey",
mustMarshalCOSEKey(t, int64(RSAKey), int64(AlgRS256), map[int64]any{-1: []byte{0xFF}, -2: []byte{0x01, 0x00, 0x01}}),
"",
},
{
"ShouldRejectOKPWithInvalidXCoordLength",
mustMarshalCOSEKey(t, int64(OctetKey), int64(AlgEdDSA), map[int64]any{-2: make([]byte, 16)}),
"OKP key x coordinate has invalid length 16, expected 32",
},
{
"ShouldRejectEC2WithUnsupportedAlgorithm",
mustMarshalCOSEKey(t, int64(EllipticKey), int64(999), map[int64]any{-1: int64(P256), -2: make([]byte, 32), -3: make([]byte, 32)}),
"Unsupported EC2 algorithm",
},
{
"ShouldRejectEC2WithInvalidCoordLength",
mustMarshalCOSEKey(t, int64(EllipticKey), int64(AlgES256), map[int64]any{-1: int64(P256), -2: make([]byte, 48), -3: make([]byte, 48)}),
"EC2 key x or y coordinate has invalid length",
},
{
"ShouldRejectEC2WithOffCurvePoint",
mustMarshalCOSEKey(t, int64(EllipticKey), int64(AlgES256), map[int64]any{-1: int64(P256), -2: make([]byte, 32), -3: make([]byte, 32)}),
"EC2 key point is not on curve",
},
{
"ShouldRejectRSAWithEmptyModulus",
mustMarshalCOSEKey(t, int64(RSAKey), int64(AlgRS256), map[int64]any{-1: []byte{}, -2: []byte{0x01, 0x00, 0x01}}),
"RSA key contains zero or empty modulus",
},
{
"ShouldRejectRSAWithEmptyExponent",
mustMarshalCOSEKey(t, int64(RSAKey), int64(AlgRS256), map[int64]any{-1: []byte{0xFF}, -2: []byte{}}),
"RSA key contains invalid exponent: invalid exponent length",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result, err := ParsePublicKey(tc.input)
if tc.err != "" {
assert.Nil(t, result)
assert.EqualError(t, err, tc.err)
} else {
assert.NoError(t, err)
assert.NotNil(t, result)
}
})
}
}
func MustExtractCBORKeyFromAttestationObject(t *testing.T, have string) []byte {
type AttObj struct {
AuthData []byte `cbor:"authData"`
}
raw := MustDecodeHex(t, have)
att := AttObj{}
require.NoError(t, cbor.Unmarshal(raw, &att))
// rpIdHash/flags/counter.
offset := 32 + 1 + 4
// AAGUID.
offset += 16
credLen := int(att.AuthData[offset])<<8 | int(att.AuthData[offset+1])
offset += 2 + credLen
return att.AuthData[offset:]
}
func TestFIDOPublicKey(t *testing.T) {
testCases := []struct {
name string
have string
clientDataJSON string
authenticatorData string
signature string
public bool
expected EC2PublicKeyData
tpmcurve tpm2.TPMECCCurve
err string
}{
{
// Test Vector: https://w3c.github.io/webauthn/#sctn-test-vectors-fido-u2f-es256
"ShouldHandleTestVector45",
"51bd002938fa10b83683ac2a2032d0a7338c7f65a90228cfd1f61b81ec7288d0",
"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a222d5178684b59485954316d554f4e3461554139326b6d36537a49532d2d4f417362694e5650774249564455222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d",
"bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b50100000000",
"304402206172459958fea907b7292b92f555034bfd884895f287a76200c1ba287239137002204727b166147e26a21bbc2921d192ebfed569b79438538e5c128b5e28e6926dd7",
false,
EC2PublicKeyData{PublicKeyData: PublicKeyData{_struct: false, KeyType: 2, Algorithm: -7}, Curve: int64(P256), XCoord: []uint8{0xb0, 0xd6, 0x2d, 0xe6, 0xb3, 0xf, 0x86, 0xf0, 0xba, 0xc7, 0xa9, 0x1, 0x69, 0x51, 0x39, 0x1c, 0x2e, 0x31, 0x84, 0x9e, 0x2e, 0x64, 0x66, 0x1c, 0xbd, 0x2b, 0x13, 0xcd, 0x7d, 0x55, 0x8, 0xad}, YCoord: []uint8{0x50, 0x3b, 0xb, 0xda, 0x2a, 0x35, 0x7a, 0x9a, 0x4b, 0x34, 0x47, 0x5a, 0x28, 0xe6, 0x5b, 0x66, 0xb, 0x48, 0x98, 0xa9, 0xe3, 0xe9, 0xbb, 0xf0, 0x82, 0xd, 0x43, 0x49, 0x42, 0x97, 0xed, 0xd0}},
tpm2.TPMECCNistP256,
"",
},
{
"ShouldNotParseTestFixture1AsPublicKey",
"51bd002938fa10b83683ac2a2032d0a7338c7f65a90228cfd1f61b81ec7288d0",
"",
"",
"",
true,
EC2PublicKeyData{},
0x00,
"failed to parse FIDO public key: crypto/ecdh: invalid public key",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var (
keyBytes []byte
result EC2PublicKeyData
err error
)
if tc.public {
keyBytes, err = hex.DecodeString(tc.have)
require.NoError(t, err)
result, err = ParseFIDOPublicKey(keyBytes)
} else {
keyBytes = DeriveEC2P256PublicKeyFromPrivateKey(t, tc.have)
result, err = ParseFIDOPublicKey(keyBytes)
}
assert.Equal(t, tc.expected, result)
if tc.err != "" {
assert.EqualError(t, err, tc.err)
} else {
assert.NoError(t, err)
key, err := result.ToECDSA()
require.NoError(t, err)
assert.NotNil(t, key)
assert.Equal(t, tc.tpmcurve, result.TPMCurveID())
data := MustConstructSignedData(t, tc.clientDataJSON, tc.authenticatorData)
signature := MustDecodeHex(t, tc.signature)
ok, err := VerifySignature(result, data, signature)
assert.NoError(t, err)
assert.True(t, ok)
}
})
}
}
func MustConstructSignedData(t *testing.T, hexClientDataJSON, hexAuthenticatorData string) []byte {
clientDataJSON := MustDecodeHex(t, hexClientDataJSON)
authenticatorData := MustDecodeHex(t, hexAuthenticatorData)
sum := sha256.Sum256(clientDataJSON)
data := make([]byte, 0, len(authenticatorData)+len(sum))
data = append(data, authenticatorData...)
data = append(data, sum[:]...)
return data
}
func MustDecodeHex(t *testing.T, s string) []byte {
b, err := hex.DecodeString(s)
require.NoError(t, err)
return b
}
func DeriveEC2P256PublicKeyFromPrivateKey(t *testing.T, keyHex string) []byte {
key, err := hex.DecodeString(keyHex)
require.NoError(t, err)
require.Len(t, key, 32)
curve := ecdh.P256()
private, err := curve.NewPrivateKey(key)
require.NoError(t, err)
public, ok := private.Public().(*ecdh.PublicKey)
require.True(t, ok)
return public.Bytes()
}