This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

Implement Thumbprint (RFC 7638)

  • Loading branch information...
lestrrat committed Dec 7, 2015
1 parent e3f0caf commit 53645af99a1b910ad77cf6c8eced4916d658aa3a
Showing with 117 additions and 7 deletions.
  1. +9 −7 README.md
  2. +31 −0 jwk/ecdsa.go
  3. +5 −0 jwk/interface.go
  4. +21 −0 jwk/rsa.go
  5. +31 −0 jwk/rsa_test.go
  6. +20 −0 jwk/symmetric.go
View
@@ -7,13 +7,15 @@
### Done
PR/issues welcome. All needs more docs
* jwt
* jwk
* jwa
* jws
* jwe
PR/issues welcome.
| Package name | Notes |
|--------------|-------|
| jwt | [RFC 7519](https://tools.ietf.org/html/rfc7519) |
| jwk | [RFC 7517](https://tools.ietf.org/html/rfc7517) + [RFC 7638](https://tools.ietf.org/html/rfc7638)) |
| jwa | [RFC 7518](https://tools.ietf.org/html/rfc7518) |
| jws | [RFC 7515](https://tools.ietf.org/html/rfc7515) |
| jwe | [RFC 7516](https://tools.ietf.org/html/rfc7516) |
### In progress:
View
@@ -1,8 +1,11 @@
package jwk
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/base64"
"fmt"
"math/big"
"github.com/lestrrat/go-jwx/jwa"
@@ -49,6 +52,34 @@ func (k *EcdsaPublicKey) PublicKey() (*ecdsa.PublicKey, error) {
return pubkey, nil
}
// Thumbprint returns the JWK thumbprint using the indicated
// hashing algorithm, according to RFC 7638
func (k EcdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
const tmpl = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
csize := k.Curve.Size()
// We need to truncate the buffer at curve size
xbuf := k.X.Bytes()
if len(xbuf) > csize {
xbuf = xbuf[:csize]
}
ybuf := k.Y.Bytes()
if len(ybuf) > csize {
ybuf = ybuf[:csize]
}
enc := base64.RawURLEncoding
x64 := make([]byte, enc.EncodedLen(len(xbuf)))
enc.Encode(x64, xbuf)
y64 := make([]byte, enc.EncodedLen(len(ybuf)))
enc.Encode(y64, ybuf)
v := fmt.Sprintf(tmpl, k.Curve.String(), x64, y64)
h := hash.New()
h.Write([]byte(v))
return h.Sum(nil), nil
}
func (k *EcdsaPrivateKey) Materialize() (interface{}, error) {
return k.PrivateKey()
}
View
@@ -1,6 +1,7 @@
package jwk
import (
"crypto"
"errors"
"net/url"
@@ -62,6 +63,10 @@ type Key interface {
// EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey,
// and OctetSeq types create a []byte key.
Materialize() (interface{}, error)
// Thumbprint returns the JWK thumbprint using the indicated
// hashing algorithm, according to RFC 7638
Thumbprint(crypto.Hash) ([]byte, error)
}
// EssentialHeader defines the common data that any Key may
View
@@ -1,8 +1,10 @@
package jwk
import (
"crypto"
"crypto/rsa"
"errors"
"fmt"
"math/big"
"github.com/lestrrat/go-jwx/buffer"
@@ -56,6 +58,25 @@ func (k *RsaPublicKey) PublicKey() (*rsa.PublicKey, error) {
}, nil
}
// Thumbprint returns the JWK thumbprint using the indicated
// hashing algorithm, according to RFC 7638
func (k RsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
const tmpl = `{"e":"%s","kty":"RSA","n":"%s"}`
e64, err := k.E.Base64Encode()
if err != nil {
return nil, err
}
n64, err := k.N.Base64Encode()
if err != nil {
return nil, err
}
v := fmt.Sprintf(tmpl, e64, n64)
h := hash.New()
h.Write([]byte(v))
return h.Sum(nil), nil
}
func (k *RsaPrivateKey) Materialize() (interface{}, error) {
return k.PrivateKey()
}
View
@@ -1,9 +1,11 @@
package jwk
import (
"crypto"
"crypto/rsa"
"testing"
"github.com/lestrrat/go-jwx/buffer"
"github.com/stretchr/testify/assert"
)
@@ -90,3 +92,32 @@ func TestParse_RsaPrivateKey(t *testing.T) {
return
}
}
func TestThumbprint(t *testing.T) {
expected := []byte{55, 54, 203, 177, 120, 124, 184, 48, 156, 119, 238,
140, 55, 5, 197, 225, 111, 251, 158, 133, 151, 21, 144, 31, 30, 76, 89,
177, 17, 130, 245, 123,
}
n, err := buffer.FromBase64([]byte("0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"))
if !assert.NoError(t, err, "decode N succeeds") {
return
}
e, err := buffer.FromBase64([]byte("AQAB"))
if !assert.NoError(t, err, "decode E succeeds") {
return
}
key := RsaPublicKey{
EssentialHeader: &EssentialHeader{KeyType: "RSA"},
N: n,
E: e,
}
tp, err := key.Thumbprint(crypto.SHA256)
if !assert.NoError(t, err, "Thumbprint should succeed") {
return
}
if !assert.Equal(t, expected, tp, "Thumbprint should match") {
return
}
}
View
@@ -1,9 +1,29 @@
package jwk
import (
"crypto"
"fmt"
)
func (s SymmetricKey) Materialize() (interface{}, error) {
return s.Octets(), nil
}
func (s SymmetricKey) Octets() []byte {
return s.Key
}
// Thumbprint returns the JWK thumbprint using the indicated
// hashing algorithm, according to RFC 7638
func (s SymmetricKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
const tmpl = `{"k":"%s","kty":"oct"}`
k64, err := s.Key.Base64Encode()
if err != nil {
return nil, err
}
v := fmt.Sprintf(tmpl, k64)
h := hash.New()
h.Write([]byte(v))
return h.Sum(nil), nil
}

0 comments on commit 53645af

Please sign in to comment.