8000 fix: implement JWK thumbprint for Ed25519 public keys by zhangyoufu · Pull Request #4626 · distribution/distribution · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix: implement JWK thumbprint for Ed25519 public keys #4626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/content/about/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ Default `signingalgorithms`:
Additional notes on `rootcertbundle`:

- The public key of this certificate will be automatically added to the list of known keys.
- The public key will be identified by it's [RFC7638 Thumbprint](https://datatracker.ietf.org/doc/html/rfc7638).
- The public key will be identified by its JWK Thumbprint. See [RFC 7638](https://datatracker.ietf.org/doc/html/rfc7638) and [RFC 8037](https://datatracker.ietf.org/doc/html/rfc8037) for reference.

For more information about Token based authentication configuration, see the
[specification](../spec/auth/token.md).
Expand Down
2 changes: 1 addition & 1 deletion registry/auth/token/accesscontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
rootPool := x509.NewCertPool()
for _, rootCert := range rootCerts {
rootPool.AddCert(rootCert)
if key := GetRFC7638Thumbprint(rootCert.PublicKey); key != "" {
if key := GetJWKThumbprint(rootCert.PublicKey); key != "" {
trustedKeys[key] = rootCert.PublicKey
}
}
Expand Down
31 changes: 24 additions & 7 deletions registry/auth/token/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package token
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
Expand Down Expand Up @@ -54,33 +55,49 @@ func hashAndEncode(payload string) string {
return base64.RawURLEncoding.EncodeToString(shasum[:])
}

// Deprecated: use GetJWKThumbprint instead.
func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
return getJWKThumbprint(publickey, true)
}

// GetJWKThumbprint calculates the JWK thumbprint of a public key.
// The current implementation uses SHA256, but this algorithm may change
// in the future as cryptographic best practices evolve. It returns an
// empty string if the public key type is not supported.
func GetJWKThumbprint(publickey crypto.PublicKey) string {
return getJWKThumbprint(publickey, false)
}

// RFC7638 states in section 3 sub 1 that the keys in the JSON object payload
// are required to be ordered lexicographical order. Golang does not guarantee
// order of keys[0]
// [0]: https://groups.google.com/g/golang-dev/c/zBQwhm3VfvU
//
// The payloads are small enough to create the JSON strings manually
func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
var payload string

func getJWKThumbprint(publickey crypto.PublicKey, skipED25519 bool) string {
switch pubkey := publickey.(type) {
case *rsa.PublicKey:
e_big := big.NewInt(int64(pubkey.E)).Bytes()

e := base64.RawURLEncoding.EncodeToString(e_big)
n := base64.RawURLEncoding.EncodeToString(pubkey.N.Bytes())

payload = fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, e, n)
return hashAndEncode(fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, e, n))
case *ecdsa.PublicKey:
params := pubkey.Params()
crv := params.Name
x := base64.RawURLEncoding.EncodeToString(params.Gx.Bytes())
y := base64.RawURLEncoding.EncodeToString(params.Gy.Bytes())

payload = fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, crv, x, y)
return hashAndEncode(fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, crv, x, y))
case ed25519.PublicKey:
if skipED25519 {
return ""
}
x := base64.RawURLEncoding.EncodeToString(pubkey)

return hashAndEncode(fmt.Sprintf(`{"crv":"Ed25519","kty":"OTP","x":"%s"}`, x))
default:
return ""
}

return hashAndEncode(payload)
}
Loading
0