diff --git a/oidc/jose.go b/oidc/jose.go index 8afa895c..b7bd0927 100644 --- a/oidc/jose.go +++ b/oidc/jose.go @@ -13,4 +13,5 @@ const ( PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 + EdDSA = "EdDSA" // Ed25519 using SHA-512 ) diff --git a/oidc/jwks.go b/oidc/jwks.go index 524f4e67..539933b3 100644 --- a/oidc/jwks.go +++ b/oidc/jwks.go @@ -4,6 +4,7 @@ import ( "context" "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/rsa" "errors" "fmt" @@ -32,6 +33,7 @@ func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, switch pub.(type) { case *rsa.PublicKey: case *ecdsa.PublicKey: + case ed25519.PublicKey: default: return nil, fmt.Errorf("invalid public key type provided: %T", pub) } diff --git a/oidc/jwks_test.go b/oidc/jwks_test.go index 5bdbdc8e..704bdc33 100644 --- a/oidc/jwks_test.go +++ b/oidc/jwks_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -79,6 +80,14 @@ func newECDSAKey(t *testing.T) *signingKey { return &signingKey{"", priv, priv.Public(), jose.ES256} } +func newEdDSAKey(t *testing.T) *signingKey { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + return &signingKey{"", privateKey, publicKey, jose.EdDSA} +} + func TestRSAVerify(t *testing.T) { good := newRSAKey(t) bad := newRSAKey(t) @@ -92,6 +101,12 @@ func TestECDSAVerify(t *testing.T) { testKeyVerify(t, good, bad, good) } +func TestEdDSAVerify(t *testing.T) { + good := newEdDSAKey(t) + bad := newEdDSAKey(t) + testKeyVerify(t, good, bad, good) +} + func TestMultipleKeysVerify(t *testing.T) { key1 := newRSAKey(t) key2 := newRSAKey(t) diff --git a/oidc/oidc.go b/oidc/oidc.go index 3026c77e..b159d1cc 100644 --- a/oidc/oidc.go +++ b/oidc/oidc.go @@ -149,6 +149,7 @@ var supportedAlgorithms = map[string]bool{ PS256: true, PS384: true, PS512: true, + EdDSA: true, } // ProviderConfig allows creating providers when discovery isn't supported. It's @@ -448,7 +449,7 @@ func (i *IDToken) VerifyAccessToken(accessToken string) error { h = sha256.New() case RS384, ES384, PS384: h = sha512.New384() - case RS512, ES512, PS512: + case RS512, ES512, PS512, EdDSA: h = sha512.New() default: return fmt.Errorf("oidc: unsupported signing algorithm %q", i.sigAlgorithm) diff --git a/oidc/oidc_test.go b/oidc/oidc_test.go index 342cf075..8a813e3a 100644 --- a/oidc/oidc_test.go +++ b/oidc/oidc_test.go @@ -90,6 +90,12 @@ func TestAccessTokenVerification(t *testing.T) { googleAccessToken, assertMsg("id token did not have an access token hash"), }, + { + "EdDSA", + newToken("EdDSA", computed512TokenHash), + googleAccessToken, + assertNil, + }, { "badSignAlgo", newToken("none", "xxx"), @@ -135,11 +141,11 @@ func TestNewProvider(t *testing.T) { "authorization_endpoint": "https://example.com/auth", "token_endpoint": "https://example.com/token", "jwks_uri": "https://example.com/keys", - "id_token_signing_alg_values_supported": ["RS256", "RS384", "ES256"] + "id_token_signing_alg_values_supported": ["RS256", "RS384", "ES256", "EdDSA"] }`, wantAuthURL: "https://example.com/auth", wantTokenURL: "https://example.com/token", - wantAlgorithms: []string{"RS256", "RS384", "ES256"}, + wantAlgorithms: []string{"RS256", "RS384", "ES256", "EdDSA"}, }, { name: "unsupported_algorithms", diff --git a/oidc/verify_test.go b/oidc/verify_test.go index 3a84b918..f2e2433b 100644 --- a/oidc/verify_test.go +++ b/oidc/verify_test.go @@ -25,6 +25,16 @@ func TestVerify(t *testing.T) { }, signKey: newRSAKey(t), }, + { + name: "good eddsa token", + idToken: `{"iss":"https://foo"}`, + config: Config{ + SkipClientIDCheck: true, + SkipExpiryCheck: true, + SupportedSigningAlgs: []string{EdDSA}, + }, + signKey: newEdDSAKey(t), + }, { name: "invalid issuer", issuer: "https://bar", @@ -214,6 +224,16 @@ func TestVerifySigningAlg(t *testing.T) { }, signKey: newECDSAKey(t), }, + { + name: "eddsa signing", + idToken: `{"iss":"https://foo"}`, + config: Config{ + SkipClientIDCheck: true, + SkipExpiryCheck: true, + SupportedSigningAlgs: []string{EdDSA}, + }, + signKey: newEdDSAKey(t), + }, { name: "one of many supported", idToken: `{"iss":"https://foo"}`,