Skip to content

Commit 314c2be

Browse files
fix(protocol): bind credential public key curve to its algorithm (#752)
§5.8.5 requires a credential public key using ES256, ES384, ES512 or EdDSA to specify the matching curve, but the curve was never checked against the algorithm and the OKP structure carried no CBOR tag on its curve member, so the parameter was discarded while decoding and the member was permanently zero. The curve is now decoded and validated for both the EC2 and OKP key types before the key material is examined. The fully specified algorithms ESP256, ESP384, ESP512 and Ed25519 name their curve themselves, so a key using one may omit the parameter, though a curve it does carry may not contradict the algorithm. Stored keys are re-parsed on every assertion, so this applies to authentication as well as registration. COSE requires the parameter for both key types and the specification test vectors carry it, so only a key which contradicts itself is newly rejected. COSEAlgorithmIdentifier and COSEEllipticCurve gain a String method returning the short name each is registered under, so a rejection names what it saw rather than a bare identifier, and each enumeration moves to its own file.
1 parent 2c1e2ec commit 314c2be

7 files changed

Lines changed: 497 additions & 176 deletions

File tree

protocol/webauthncose/const.go

Lines changed: 32 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -6,132 +6,39 @@ const (
66

77
const ecCoordSize = 32
88

9-
// COSEAlgorithmIdentifier is a number identifying a cryptographic algorithm. The algorithm identifiers SHOULD be values
10-
// registered in the IANA COSE Algorithms registry [https://www.w3.org/TR/webauthn/#biblio-iana-cose-algs-reg], for
11-
// instance, -7 for "ES256" and -257 for "RS256".
12-
//
13-
// Specification: §5.8.5. Cryptographic Algorithm Identifier (https://www.w3.org/TR/webauthn/#sctn-alg-identifier)
14-
type COSEAlgorithmIdentifier int
15-
16-
const (
17-
// AlgES256 ECDSA with SHA-256.
18-
AlgES256 COSEAlgorithmIdentifier = -7
19-
20-
// AlgEdDSA EdDSA.
21-
AlgEdDSA COSEAlgorithmIdentifier = -8
22-
23-
// AlgESP256 is ECDSA using P-256 curve with pre-hashed SHA-256 input.
24-
AlgESP256 COSEAlgorithmIdentifier = -9
25-
26-
// AlgEd25519 is EdDSA using the Ed25519 curve specifically. Unlike [AlgEdDSA] which is the generic EdDSA
27-
// identifier, this explicitly specifies the Ed25519 curve.
28-
AlgEd25519 COSEAlgorithmIdentifier = -19
29-
30-
// AlgES384 ECDSA with SHA-384.
31-
AlgES384 COSEAlgorithmIdentifier = -35
32-
33-
// AlgES512 ECDSA with SHA-512.
34-
AlgES512 COSEAlgorithmIdentifier = -36
35-
36-
// AlgPS256 RSASSA-PSS with SHA-256.
37-
AlgPS256 COSEAlgorithmIdentifier = -37
38-
39-
// AlgPS384 RSASSA-PSS with SHA-384.
40-
AlgPS384 COSEAlgorithmIdentifier = -38
41-
42-
// AlgPS512 RSASSA-PSS with SHA-512.
43-
AlgPS512 COSEAlgorithmIdentifier = -39
44-
45-
// AlgES256K is ECDSA using secp256k1 curve and SHA-256.
46-
AlgES256K COSEAlgorithmIdentifier = -47
47-
48-
// AlgMLDSA44 is ML-DSA with parameter set ML-DSA-44 (FIPS 204).
49-
AlgMLDSA44 COSEAlgorithmIdentifier = -48
50-
51-
// AlgMLDSA65 is ML-DSA with parameter set ML-DSA-65 (FIPS 204).
52-
AlgMLDSA65 COSEAlgorithmIdentifier = -49
53-
54-
// AlgMLDSA87 is ML-DSA with parameter set ML-DSA-87 (FIPS 204).
55-
AlgMLDSA87 COSEAlgorithmIdentifier = -50
56-
57-
// AlgESP384 is ECDSA using P-384 curve with pre-hashed SHA-384 input.
58-
AlgESP384 COSEAlgorithmIdentifier = -51
59-
60-
// AlgESP512 is ECDSA using P-521 curve with pre-hashed SHA-512 input.
61-
AlgESP512 COSEAlgorithmIdentifier = -52
62-
63-
// AlgRS256 RSASSA-PKCS1-v1_5 with SHA-256.
64-
AlgRS256 COSEAlgorithmIdentifier = -257
65-
66-
// AlgRS384 RSASSA-PKCS1-v1_5 with SHA-384.
67-
AlgRS384 COSEAlgorithmIdentifier = -258
68-
69-
// AlgRS512 RSASSA-PKCS1-v1_5 with SHA-512.
70-
AlgRS512 COSEAlgorithmIdentifier = -259
71-
72-
// AlgRS1 RSASSA-PKCS1-v1_5 with SHA-1.
73-
AlgRS1 COSEAlgorithmIdentifier = -65535
9+
type Error struct {
10+
// Short name for the type of error that has occurred.
11+
Type string `json:"type"`
12+
13+
// Additional details about the error.
14+
Details string `json:"error"`
15+
16+
// Information to help debug the error.
17+
DevInfo string `json:"debug"`
18+
}
19+
20+
var (
21+
ErrUnsupportedKey = &Error{
22+
Type: "invalid_key_type",
23+
Details: "Unsupported Public Key Type",
24+
}
25+
ErrUnsupportedAlgorithm = &Error{
26+
Type: "unsupported_key_algorithm",
27+
Details: "Unsupported public key algorithm",
28+
}
29+
ErrSigNotProvidedOrInvalid = &Error{
30+
Type: "signature_not_provided_or_invalid",
31+
Details: "Signature invalid or not provided",
32+
}
7433
)
7534

76-
// COSEKeyType is The Key type derived from the IANA COSE AuthData.
77-
type COSEKeyType int
35+
func (err *Error) Error() string {
36+
return err.Details
37+
}
7838

79-
const (
80-
// KeyTypeReserved is a reserved value.
81-
KeyTypeReserved COSEKeyType = iota
82-
83-
// OctetKey is an Octet Key.
84-
OctetKey
85-
86-
// EllipticKey is an Elliptic Curve Public Key.
87-
EllipticKey
88-
89-
// RSAKey is an RSA Public Key.
90-
RSAKey
91-
92-
// Symmetric Keys.
93-
Symmetric
94-
95-
// HSSLMS is the public key for HSS/LMS hash-based digital signature.
96-
HSSLMS
97-
98-
// WalnutDSA is the public key for Walnut Digital Signature Algorithm.
99-
WalnutDSA
100-
101-
// AKP is the key type for algorithm key pairs (i.e. ML-DSA).
102-
AKP
103-
)
104-
105-
// COSEEllipticCurve is an enumerator that represents the COSE Elliptic Curves.
106-
//
107-
// Specification: https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
108-
type COSEEllipticCurve int
39+
func (passedError *Error) WithDetails(details string) *Error {
40+
err := *passedError
41+
err.Details = details
10942

110-
const (
111-
// EllipticCurveReserved is the COSE EC Reserved value.
112-
EllipticCurveReserved COSEEllipticCurve = iota
113-
114-
// P256 represents NIST P-256 also known as secp256r1.
115-
P256
116-
117-
// P384 represents NIST P-384 also known as secp384r1.
118-
P384
119-
120-
// P521 represents NIST P-521 also known as secp521r1.
121-
P521
122-
123-
// X25519 for use w/ ECDH only.
124-
X25519
125-
126-
// X448 for use w/ ECDH only.
127-
X448
128-
129-
// Ed25519 for use w/ EdDSA only.
130-
Ed25519
131-
132-
// Ed448 for use w/ EdDSA only.
133-
Ed448
134-
135-
// Secp256k1 is the SECG secp256k1 curve.
136-
Secp256k1
137-
)
43+
return &err
44+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package webauthncose
2+
3+
import (
4+
"strconv"
5+
"crypto"
6+
"crypto/x509"
7+
)
8+
9+
// COSEAlgorithmIdentifier is a number identifying a cryptographic algorithm. The algorithm identifiers SHOULD be values
10+
// registered in the IANA COSE Algorithms registry [https://www.w3.org/TR/webauthn/#biblio-iana-cose-algs-reg], for
11+
// instance, -7 for "ES256" and -257 for "RS256".
12+
//
13+
// Specification: §5.8.5. Cryptographic Algorithm Identifier (https://www.w3.org/TR/webauthn/#sctn-alg-identifier)
14+
type COSEAlgorithmIdentifier int
15+
16+
const (
17+
// AlgES256 ECDSA with SHA-256.
18+
AlgES256 COSEAlgorithmIdentifier = -7
19+
20+
// AlgEdDSA EdDSA.
21+
AlgEdDSA COSEAlgorithmIdentifier = -8
22+
23+
// AlgESP256 is ECDSA using P-256 curve with pre-hashed SHA-256 input.
24+
AlgESP256 COSEAlgorithmIdentifier = -9
25+
26+
// AlgEd25519 is EdDSA using the Ed25519 curve specifically. Unlike [AlgEdDSA] which is the generic EdDSA
27+
// identifier, this explicitly specifies the Ed25519 curve.
28+
AlgEd25519 COSEAlgorithmIdentifier = -19
29+
30+
// AlgES384 ECDSA with SHA-384.
31+
AlgES384 COSEAlgorithmIdentifier = -35
32+
33+
// AlgES512 ECDSA with SHA-512.
34+
AlgES512 COSEAlgorithmIdentifier = -36
35+
36+
// AlgPS256 RSASSA-PSS with SHA-256.
37+
AlgPS256 COSEAlgorithmIdentifier = -37
38+
39+
// AlgPS384 RSASSA-PSS with SHA-384.
40+
AlgPS384 COSEAlgorithmIdentifier = -38
41+
42+
// AlgPS512 RSASSA-PSS with SHA-512.
43+
AlgPS512 COSEAlgorithmIdentifier = -39
44+
45+
// AlgES256K is ECDSA using secp256k1 curve and SHA-256.
46+
AlgES256K COSEAlgorithmIdentifier = -47
47+
48+
// AlgMLDSA44 is ML-DSA with parameter set ML-DSA-44 (FIPS 204).
49+
AlgMLDSA44 COSEAlgorithmIdentifier = -48
50+
51+
// AlgMLDSA65 is ML-DSA with parameter set ML-DSA-65 (FIPS 204).
52+
AlgMLDSA65 COSEAlgorithmIdentifier = -49
53+
54+
// AlgMLDSA87 is ML-DSA with parameter set ML-DSA-87 (FIPS 204).
55+
AlgMLDSA87 COSEAlgorithmIdentifier = -50
56+
57+
// AlgESP384 is ECDSA using P-384 curve with pre-hashed SHA-384 input.
58+
AlgESP384 COSEAlgorithmIdentifier = -51
59+
60+
// AlgESP512 is ECDSA using P-521 curve with pre-hashed SHA-512 input.
61+
AlgESP512 COSEAlgorithmIdentifier = -52
62+
63+
// AlgRS256 RSASSA-PKCS1-v1_5 with SHA-256.
64+
AlgRS256 COSEAlgorithmIdentifier = -257
65+
66+
// AlgRS384 RSASSA-PKCS1-v1_5 with SHA-384.
67+
AlgRS384 COSEAlgorithmIdentifier = -258
68+
69+
// AlgRS512 RSASSA-PKCS1-v1_5 with SHA-512.
70+
AlgRS512 COSEAlgorithmIdentifier = -259
71+
72+
// AlgRS1 RSASSA-PKCS1-v1_5 with SHA-1.
73+
AlgRS1 COSEAlgorithmIdentifier = -65535
74+
)
75+
76+
// String returns the short name under which the algorithm is registered, falling back to its numeric identifier for
77+
// an algorithm this library does not model.
78+
//
79+
// This is deliberately distinct from the name member of [COSESignatureAlgorithmDetails], which describes the
80+
// primitives the algorithm composes (i.e. "ECDSA-SHA256") rather than naming the algorithm itself.
81+
//
82+
// Registry: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
83+
func (a COSEAlgorithmIdentifier) String() string {
84+
if name, ok := coseAlgorithmNames[a]; ok {
85+
return name
86+
}
87+
88+
return strconv.Itoa(int(a))
89+
}
90+
91+
// coseAlgorithmNames maps each algorithm identifier this library models to the short name under which it is
92+
// registered, backing [COSEAlgorithmIdentifier.String].
93+
//
94+
// Registry: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
95+
var coseAlgorithmNames = map[COSEAlgorithmIdentifier]string{
96+
AlgES256: "ES256",
97+
AlgEdDSA: "EdDSA",
98+
AlgESP256: "ESP256",
99+
AlgEd25519: "Ed25519",
100+
AlgES384: "ES384",
101+
AlgES512: "ES512",
102+
AlgPS256: "PS256",
103+
AlgPS384: "PS384",
104+
AlgPS512: "PS512",
105+
AlgES256K: "ES256K",
106+
AlgMLDSA44: "ML-DSA-44",
107+
AlgMLDSA65: "ML-DSA-65",
108+
AlgMLDSA87: "ML-DSA-87",
109+
AlgESP384: "ESP384",
110+
AlgESP512: "ESP512",
111+
AlgRS256: "RS256",
112+
AlgRS384: "RS384",
113+
AlgRS512: "RS512",
114+
AlgRS1: "RS1",
115+
}
116+
117+
var COSESignatureAlgorithmDetails = map[COSEAlgorithmIdentifier]struct {
118+
name string
119+
hash crypto.Hash
120+
sigAlg x509.SignatureAlgorithm
121+
}{
122+
AlgRS1: {"SHA1-RSA", crypto.SHA1, x509.SHA1WithRSA},
123+
AlgRS256: {"SHA256-RSA", crypto.SHA256, x509.SHA256WithRSA},
124+
AlgRS384: {"SHA384-RSA", crypto.SHA384, x509.SHA384WithRSA},
125+
AlgRS512: {"SHA512-RSA", crypto.SHA512, x509.SHA512WithRSA},
126+
AlgPS256: {"SHA256-RSAPSS", crypto.SHA256, x509.SHA256WithRSAPSS},
127+
AlgPS384: {"SHA384-RSAPSS", crypto.SHA384, x509.SHA384WithRSAPSS},
128+
AlgPS512: {"SHA512-RSAPSS", crypto.SHA512, x509.SHA512WithRSAPSS},
129+
AlgES256: {"ECDSA-SHA256", crypto.SHA256, x509.ECDSAWithSHA256},
130+
AlgESP256: {"ECDSA-SHA256-Prehashed", crypto.SHA256, x509.ECDSAWithSHA256},
131+
AlgES384: {"ECDSA-SHA384", crypto.SHA384, x509.ECDSAWithSHA384},
132+
AlgESP384: {"ECDSA-SHA384-Prehashed", crypto.SHA384, x509.ECDSAWithSHA384},
133+
AlgES512: {"ECDSA-SHA512", crypto.SHA512, x509.ECDSAWithSHA512},
134+
AlgESP512: {"ECDSA-SHA512-Prehashed", crypto.SHA512, x509.ECDSAWithSHA512},
135+
AlgEdDSA: {"EdDSA", crypto.SHA512, x509.PureEd25519},
136+
AlgEd25519: {"Ed25519", crypto.SHA512, x509.PureEd25519},
137+
}
138+
139+
// coseAlgorithmCurves binds each elliptic curve algorithm this library verifies with to the curve §5.8.5 requires a
140+
// key using that algorithm to specify.
141+
//
142+
// The specification requires the crv parameter for ES256, ES384, ES512 and EdDSA. It states no requirement for the
143+
// fully specified algorithms ESP256, ESP384, ESP512 and Ed25519, because those identifiers name their curve
144+
// themselves and leave nothing for the parameter to settle. A crv such a key does carry is still held to the curve
145+
// its algorithm names, as a key which contradicts itself is malformed however the requirement is written.
146+
//
147+
// Specification: §5.8.5. Cryptographic Algorithm Identifier (https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier)
148+
var coseAlgorithmCurves = map[COSEAlgorithmIdentifier]coseAlgorithmCurve{
149+
AlgES256: {curve: P256, required: true},
150+
AlgES384: {curve: P384, required: true},
151+
AlgES512: {curve: P521, required: true},
152+
AlgEdDSA: {curve: Ed25519, required: true},
153+
AlgESP256: {curve: P256},
154+
AlgESP384: {curve: P384},
155+
AlgESP512: {curve: P521},
156+
AlgEd25519: {curve: Ed25519},
157+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package webauthncose
2+
3+
import "strconv"
4+
5+
// COSEEllipticCurve is an enumerator that represents the COSE Elliptic Curves.
6+
//
7+
// Specification: https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
8+
type COSEEllipticCurve int
9+
10+
const (
11+
// EllipticCurveReserved is the COSE EC Reserved value.
12+
EllipticCurveReserved COSEEllipticCurve = iota
13+
14+
// P256 represents NIST P-256 also known as secp256r1.
15+
P256
16+
17+
// P384 represents NIST P-384 also known as secp384r1.
18+
P384
19+
20+
// P521 represents NIST P-521 also known as secp521r1.
21+
P521
22+
23+
// X25519 for use w/ ECDH only.
24+
X25519
25+
26+
// X448 for use w/ ECDH only.
27+
X448
28+
29+
// Ed25519 for use w/ EdDSA only.
30+
Ed25519
31+
32+
// Ed448 for use w/ EdDSA only.
33+
Ed448
34+
35+
// Secp256k1 is the SECG secp256k1 curve.
36+
Secp256k1
37+
)
38+
39+
// String returns the name under which the curve is registered, falling back to its numeric identifier for a curve
40+
// this library does not model.
41+
//
42+
// Registry: https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
43+
func (c COSEEllipticCurve) String() string {
44+
if name, ok := coseEllipticCurveNames[c]; ok {
45+
return name
46+
}
47+
48+
return strconv.Itoa(int(c))
49+
}
50+
51+
// coseEllipticCurveNames maps each curve this library models to the name under which it is registered, backing
52+
// [COSEEllipticCurve.String].
53+
//
54+
// Registry: https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
55+
var coseEllipticCurveNames = map[COSEEllipticCurve]string{
56+
P256: "P-256",
57+
P384: "P-384",
58+
P521: "P-521",
59+
X25519: "X25519",
60+
X448: "X448",
61+
Ed25519: "Ed25519",
62+
Ed448: "Ed448",
63+
Secp256k1: "secp256k1",
64+
}

0 commit comments

Comments
 (0)