diff --git a/registry/auth/token/token.go b/registry/auth/token/token.go index 55d0b7f6f..0773d5307 100644 --- a/registry/auth/token/token.go +++ b/registry/auth/token/token.go @@ -212,18 +212,18 @@ func verifyCertChain(header jose.Header, roots *x509.CertPool) (signingKey crypt return } -func verifyJWK(header jose.Header, verifyOpts VerifyOptions) (signingKey crypto.PublicKey, err error) { +func verifyJWK(header jose.Header, verifyOpts VerifyOptions) (crypto.PublicKey, error) { jwk := header.JSONWebKey - signingKey = jwk.Key // Check to see if the key includes a certificate chain. if len(jwk.Certificates) == 0 { // The JWK should be one of the trusted root keys. - if _, trusted := verifyOpts.TrustedKeys[jwk.KeyID]; !trusted { + key, trusted := verifyOpts.TrustedKeys[jwk.KeyID] + if !trusted { return nil, errors.New("untrusted JWK with no certificate chain") } // The JWK is one of the trusted keys. - return + return key, nil } opts := x509.VerifyOptions{ @@ -245,9 +245,8 @@ func verifyJWK(header jose.Header, verifyOpts VerifyOptions) (signingKey crypto. if err != nil { return nil, err } - signingKey = getCertPubKey(chains) - return + return getCertPubKey(chains), nil } func getCertPubKey(chains [][]*x509.Certificate) crypto.PublicKey { diff --git a/registry/auth/token/token_test.go b/registry/auth/token/token_test.go index b982ab14b..ba9f073cb 100644 --- a/registry/auth/token/token_test.go +++ b/registry/auth/token/token_test.go @@ -646,3 +646,57 @@ func TestNewAccessControllerPemBlock(t *testing.T) { t.Fatal("accessController has the wrong number of certificates") } } + +// This test makes sure the untrusted key can not be used in token verification. +func TestVerifyJWKWithTrustedKey(t *testing.T) { + // Generate a test key pair + privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + pubKey := privKey.Public() + + // Create a JWK with no certificates + jwk := &jose.JSONWebKey{ + Key: privKey, + KeyID: "test-key-id", + Use: "sig", + Algorithm: string(jose.ES256), + } + + // Create verify options with our public key as trusted + verifyOpts := VerifyOptions{ + TrustedKeys: map[string]crypto.PublicKey{ + "test-key-id": pubKey, + }, + } + + // Create test header + header := jose.Header{ + JSONWebKey: jwk, + } + + // Test the verifyJWK function + returnedKey, err := verifyJWK(header, verifyOpts) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + // Verify the returned key matches our trusted key + if returnedKey != pubKey { + t.Error("Returned key does not match the trusted key") + } + + // Test with untrusted key + verifyOpts.TrustedKeys = map[string]crypto.PublicKey{ + "different-key-id": pubKey, + } + + _, err = verifyJWK(header, verifyOpts) + if err == nil { + t.Error("Expected error for untrusted key, got none") + } + if err.Error() != "untrusted JWK with no certificate chain" { + t.Errorf("Expected 'untrusted JWK with no certificate chain' error, got: %v", err) + } +}