move away from PublicKeyDeprecated

This commit is contained in:
AJ ONeal 2020-10-21 03:16:34 -06:00
parent 098f92178a
commit 743731c804
9 changed files with 132 additions and 94 deletions

View File

@ -105,7 +105,7 @@ func gen(args []string) {
key := keypairs.NewDefaultPrivateKey() key := keypairs.NewDefaultPrivateKey()
marshalPriv(key, keyname) marshalPriv(key, keyname)
pub := keypairs.NewPublicKey(key.Public()) pub := key.Public().(keypairs.PublicKeyTransitional)
marshalPub(pub, pubname) marshalPub(pub, pubname)
} }
@ -255,33 +255,33 @@ func readKey(keyname string) (keypairs.PrivateKey, error) {
return key, nil return key, nil
} }
func readPub(pubname string) (keypairs.PublicKey, error) { func readPub(pubname string) (keypairs.PublicKeyTransitional, error) {
var pub keypairs.PublicKey = nil var pub keypairs.PublicKeyTransitional = nil
// Read as file // Read as file
b, err := ioutil.ReadFile(pubname) b, err := ioutil.ReadFile(pubname)
if nil != err { if nil != err {
// No file? Try as string! // No file? Try as string!
var err2 error pub2, err2 := keypairs.ParsePublicKey([]byte(pubname))
pub, err2 = keypairs.ParsePublicKey([]byte(pubname))
if nil != err2 { if nil != err2 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"could not read public key as file (or parse as string) %q:\n%w", "could not read public key as file (or parse as string) %q:\n%w",
pubname, err, pubname, err,
) )
} }
pub = pub2.Key().(keypairs.PublicKeyTransitional)
} }
// Oh, it was a file. // Oh, it was a file.
if nil == pub { if nil == pub {
var err3 error pub3, err3 := keypairs.ParsePublicKey(b)
pub, err3 = keypairs.ParsePublicKey(b)
if nil != err3 { if nil != err3 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"could not parse public key from file %q:\n%w", "could not parse public key from file %q:\n%w",
pubname, err3, pubname, err3,
) )
} }
pub = pub3.Key().(keypairs.PublicKeyTransitional)
} }
return pub, nil return pub, nil
@ -351,7 +351,7 @@ func marshalPriv(key keypairs.PrivateKey, keyname string) {
ioutil.WriteFile(keyname, b, 0600) ioutil.WriteFile(keyname, b, 0600)
} }
func marshalPub(pub keypairs.PublicKey, pubname string) { func marshalPub(pub keypairs.PublicKeyTransitional, pubname string) {
var b []byte var b []byte
if "" == pubname { if "" == pubname {
b = indentJSON(keypairs.MarshalJWKPublicKey(pub)) b = indentJSON(keypairs.MarshalJWKPublicKey(pub))
@ -363,9 +363,9 @@ func marshalPub(pub keypairs.PublicKey, pubname string) {
if strings.HasSuffix(pubname, ".json") { if strings.HasSuffix(pubname, ".json") {
b = indentJSON(keypairs.MarshalJWKPublicKey(pub)) b = indentJSON(keypairs.MarshalJWKPublicKey(pub))
} else if strings.HasSuffix(pubname, ".pem") { } else if strings.HasSuffix(pubname, ".pem") {
b, _ = keypairs.MarshalPEMPublicKey(pub.Key().(keypairs.PublicKeyTransitional)) b, _ = keypairs.MarshalPEMPublicKey(pub)
} else if strings.HasSuffix(pubname, ".der") { } else if strings.HasSuffix(pubname, ".der") {
b, _ = keypairs.MarshalDERPublicKey(pub.Key().(keypairs.PublicKeyTransitional)) b, _ = keypairs.MarshalDERPublicKey(pub)
} }
ioutil.WriteFile(pubname, b, 0644) ioutil.WriteFile(pubname, b, 0644)

View File

@ -41,7 +41,7 @@ var ErrInsecureDomain = errors.New("Whitelists should only allow secure URLs (i.
// CachableKey represents // CachableKey represents
type CachableKey struct { type CachableKey struct {
Key keypairs.PublicKey Key keypairs.PublicKeyDeprecated
Expiry time.Time Expiry time.Time
} }
@ -80,7 +80,7 @@ var MinimumKeyDuration = time.Hour
var MaximumKeyDuration = 72 * time.Hour var MaximumKeyDuration = 72 * time.Hour
// PublicKeysMap is a newtype for a map of keypairs.PublicKey // PublicKeysMap is a newtype for a map of keypairs.PublicKey
type PublicKeysMap map[string]keypairs.PublicKey type PublicKeysMap = map[string]keypairs.PublicKeyDeprecated
// OIDCJWKs fetches baseURL + ".well-known/openid-configuration" and then fetches and returns the Public Keys. // OIDCJWKs fetches baseURL + ".well-known/openid-configuration" and then fetches and returns the Public Keys.
func OIDCJWKs(baseURL string) (PublicKeysMap, error) { func OIDCJWKs(baseURL string) (PublicKeysMap, error) {
@ -135,20 +135,31 @@ func JWK(kidOrThumb, iss string) (keypairs.PublicKeyTransitional, error) {
// PEM tries to return a key from cache, falling back to the specified PEM url // PEM tries to return a key from cache, falling back to the specified PEM url
func PEM(url string) (keypairs.PublicKeyTransitional, error) { func PEM(url string) (keypairs.PublicKeyTransitional, error) {
// url is kid in this case // url is kid in this case
return immediateOneOrFetch(url, url, func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { return immediateOneOrFetch(url, url, func(string) (map[string]map[string]string, map[string]keypairs.PublicKeyDeprecated, error) {
m, key, err := uncached.PEM(url) m, key, err := uncached.PEM(url)
if nil != err { if nil != err {
return nil, nil, err return nil, nil, err
} }
pubd := keypairs.NewPublicKey(key)
// TODO bring this back
switch p := pubd.(type) {
case *keypairs.ECPublicKey:
p.KID = url
case *keypairs.RSAPublicKey:
p.KID = url
default:
return nil, nil, errors.New("impossible key type")
}
// put in a map, just for caching // put in a map, just for caching
maps := map[string]map[string]string{} maps := map[string]map[string]string{}
maps[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = m maps[keypairs.Thumbprint(key)] = m
maps[url] = m maps[url] = m
keys := map[string]keypairs.PublicKey{} keys := uncached.PublicKeysMap{} // map[string]keypairs.PublicKeyDeprecated{}
keys[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = key keys[keypairs.Thumbprint(key)] = pubd
keys[url] = key keys[url] = pubd
return maps, keys, nil return maps, keys, nil
}) })
@ -157,7 +168,8 @@ func PEM(url string) (keypairs.PublicKeyTransitional, error) {
// Fetch returns a key from cache, falling back to an exact url as the "issuer" // Fetch returns a key from cache, falling back to an exact url as the "issuer"
func Fetch(url string) (keypairs.PublicKeyTransitional, error) { func Fetch(url string) (keypairs.PublicKeyTransitional, error) {
// url is kid in this case // url is kid in this case
return immediateOneOrFetch(url, url, func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { return immediateOneOrFetch(url, url,
func(string) (map[string]map[string]string, map[string]keypairs.PublicKeyDeprecated, error) {
m, key, err := uncached.Fetch(url) m, key, err := uncached.Fetch(url)
if nil != err { if nil != err {
return nil, nil, err return nil, nil, err
@ -167,7 +179,7 @@ func Fetch(url string) (keypairs.PublicKeyTransitional, error) {
maps := map[string]map[string]string{} maps := map[string]map[string]string{}
maps[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = m maps[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = m
keys := map[string]keypairs.PublicKey{} keys := map[string]keypairs.PublicKeyDeprecated{}
keys[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = key keys[keypairs.Thumbprint(key.Key().(keypairs.PublicKeyTransitional))] = key
return maps, keys, nil return maps, keys, nil
@ -176,9 +188,9 @@ func Fetch(url string) (keypairs.PublicKeyTransitional, error) {
// Get retrieves a key from cache, or returns an error. // Get retrieves a key from cache, or returns an error.
// The issuer string may be empty if using a thumbprint rather than a kid. // The issuer string may be empty if using a thumbprint rather than a kid.
func Get(kidOrThumb, iss string) keypairs.PublicKey { func Get(kidOrThumb, iss string) keypairs.PublicKeyTransitional {
if pub := get(kidOrThumb, iss); nil != pub { if pub := get(kidOrThumb, iss); nil != pub {
return pub.Key return pub.Key.Key().(keypairs.PublicKeyTransitional)
} }
return nil return nil
} }
@ -212,21 +224,21 @@ func get(kidOrThumb, iss string) *CachableKey {
func immediateOneOrFetch(kidOrThumb, iss string, fetcher myfetcher) (keypairs.PublicKeyTransitional, error) { func immediateOneOrFetch(kidOrThumb, iss string, fetcher myfetcher) (keypairs.PublicKeyTransitional, error) {
now := time.Now() now := time.Now()
key := get(kidOrThumb, iss) hit := get(kidOrThumb, iss)
if nil == key { if nil == hit {
return fetchAndSelect(kidOrThumb, iss, fetcher) return fetchAndSelect(kidOrThumb, iss, fetcher)
} }
// Fetch just a little before the key actually expires // Fetch just a little before the key actually expires
if key.Expiry.Sub(now) <= StaleTime { if hit.Expiry.Sub(now) <= StaleTime {
go fetchAndSelect(kidOrThumb, iss, fetcher) go fetchAndSelect(kidOrThumb, iss, fetcher)
} }
return key.Key.Key().(keypairs.PublicKeyTransitional), nil return hit.Key.Key().(keypairs.PublicKeyTransitional), nil
} }
type myfetcher func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) type myfetcher func(string) (map[string]map[string]string, map[string]keypairs.PublicKeyDeprecated, error)
func fetchAndSelect(id, baseURL string, fetcher myfetcher) (keypairs.PublicKeyTransitional, error) { func fetchAndSelect(id, baseURL string, fetcher myfetcher) (keypairs.PublicKeyTransitional, error) {
maps, keys, err := fetcher(baseURL) maps, keys, err := fetcher(baseURL)
@ -250,7 +262,7 @@ func fetchAndSelect(id, baseURL string, fetcher myfetcher) (keypairs.PublicKeyTr
return nil, fmt.Errorf("Key identified by '%s' was not found at %s", id, baseURL) return nil, fmt.Errorf("Key identified by '%s' was not found at %s", id, baseURL)
} }
func cacheKeys(maps map[string]map[string]string, keys map[string]keypairs.PublicKey, issuer string) { func cacheKeys(maps map[string]map[string]string, keys PublicKeysMap, issuer string) {
for i := range keys { for i := range keys {
key := keys[i] key := keys[i]
m := maps[i] m := maps[i]
@ -260,10 +272,13 @@ func cacheKeys(maps map[string]map[string]string, keys map[string]keypairs.Publi
} }
iss = normalizeIssuer(iss) iss = normalizeIssuer(iss)
cacheKey(m["kid"], iss, m["exp"], key) cacheKey(m["kid"], iss, m["exp"], key)
if 0 == len(m[uncached.URLishKey]) {
cacheKey(m[uncached.URLishKey], iss, m["exp"], key)
}
} }
} }
func cacheKey(kid, iss, expstr string, pub keypairs.PublicKey) error { func cacheKey(kid, iss, expstr string, pub keypairs.PublicKeyDeprecated) error {
var expiry time.Time var expiry time.Time
iss = normalizeIssuer(iss) iss = normalizeIssuer(iss)

View File

@ -11,13 +11,14 @@ import (
var pubkey keypairs.PublicKeyTransitional var pubkey keypairs.PublicKeyTransitional
func TestCachesKey(t *testing.T) { func TestCachesKey(t *testing.T) {
// TODO set KeyID() in cache
testCachesKey(t, "https://bigsquid.auth0.com/") testCachesKey(t, "https://bigsquid.auth0.com/")
clear() clear()
testCachesKey(t, "https://bigsquid.auth0.com") testCachesKey(t, "https://bigsquid.auth0.com")
// Get PEM // Get PEM
pubk3, err := PEM("https://bigsquid.auth0.com/pem") pubk3, err := PEM("https://bigsquid.auth0.com/pem")
if nil != err { if nil != err {
t.Fatal("Error fetching and caching key:", err) t.Fatal("[0] Error fetching and caching key:", err)
} }
thumb3 := keypairs.Thumbprint(pubk3) thumb3 := keypairs.Thumbprint(pubk3)
thumb := keypairs.Thumbprint(pubkey) thumb := keypairs.Thumbprint(pubkey)
@ -62,7 +63,7 @@ func testCachesKey(t *testing.T, url string) {
// Get with caching // Get with caching
pubkey, err = OIDCJWK(thumb, url) pubkey, err = OIDCJWK(thumb, url)
if nil != err { if nil != err {
t.Fatal("Error fetching and caching key:", err) t.Fatal("[1] Error fetching and caching key:", err)
} }
// Look in cache for each (and succeed) // Look in cache for each (and succeed)
@ -74,9 +75,13 @@ func testCachesKey(t *testing.T, url string) {
thumb = keypairs.Thumbprint(pubkey) thumb = keypairs.Thumbprint(pubkey)
if pub := Get(thumb, url); nil == pub { if pub := Get(thumb, url); nil == pub {
t.Fatal("key was not properly cached by kid", pubkey) t.Fatal("key was not properly cached by kid", pubkey)
} else {
t.Log("Key did not have an explicit KeyID")
} }
// TODO
/*
if 0 == len(keyfetch.GetID(thumb)) {
t.Log("Key did not have an explicit KeyID", thumb)
}
*/
// Get again (should be sub-ms instant) // Get again (should be sub-ms instant)
now := time.Now() now := time.Now()

View File

@ -4,6 +4,8 @@ package uncached
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"crypto/rsa"
"crypto/ecdsa"
"errors" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
@ -15,8 +17,17 @@ import (
"git.rootprojects.org/root/keypairs" "git.rootprojects.org/root/keypairs"
) )
// URLishKey is TODO
var URLishKey = "_kid_url"
// JWKMapByID is TODO
type JWKMapByID = map[string]map[string]string
// PublicKeysMap is TODO
type PublicKeysMap = map[string]keypairs.PublicKeyDeprecated
// OIDCJWKs gets the OpenID Connect configuration from the baseURL and then calls JWKs with the specified jwks_uri // OIDCJWKs gets the OpenID Connect configuration from the baseURL and then calls JWKs with the specified jwks_uri
func OIDCJWKs(baseURL string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { func OIDCJWKs(baseURL string) (JWKMapByID, PublicKeysMap, error) {
baseURL = normalizeBaseURL(baseURL) baseURL = normalizeBaseURL(baseURL)
oidcConf := struct { oidcConf := struct {
JWKSURI string `json:"jwks_uri"` JWKSURI string `json:"jwks_uri"`
@ -37,7 +48,7 @@ func OIDCJWKs(baseURL string) (map[string]map[string]string, map[string]keypairs
} }
// WellKnownJWKs calls JWKs with baseURL + /.well-known/jwks.json as constructs the jwks_uri // WellKnownJWKs calls JWKs with baseURL + /.well-known/jwks.json as constructs the jwks_uri
func WellKnownJWKs(baseURL string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { func WellKnownJWKs(baseURL string) (JWKMapByID, PublicKeysMap, error) {
baseURL = normalizeBaseURL(baseURL) baseURL = normalizeBaseURL(baseURL)
url := baseURL + ".well-known/jwks.json" url := baseURL + ".well-known/jwks.json"
@ -45,9 +56,9 @@ func WellKnownJWKs(baseURL string) (map[string]map[string]string, map[string]key
} }
// JWKs fetches and parses a jwks.json (assuming well-known format) // JWKs fetches and parses a jwks.json (assuming well-known format)
func JWKs(jwksurl string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { func JWKs(jwksurl string) (JWKMapByID, PublicKeysMap, error) {
keys := map[string]keypairs.PublicKey{} keys := PublicKeysMap{}
maps := map[string]map[string]string{} maps := JWKMapByID{}
resp := struct { resp := struct {
Keys []map[string]interface{} `json:"keys"` Keys []map[string]interface{} `json:"keys"`
}{ }{
@ -79,33 +90,39 @@ func JWKs(jwksurl string) (map[string]map[string]string, map[string]keypairs.Pub
} }
// PEM fetches and parses a PEM (assuming well-known format) // PEM fetches and parses a PEM (assuming well-known format)
func PEM(pemurl string) (map[string]string, keypairs.PublicKey, error) { func PEM(pemurl string) (map[string]string, keypairs.PublicKeyTransitional, error) {
var pub keypairs.PublicKey var pubd keypairs.PublicKeyDeprecated
if err := safeFetch(pemurl, func(body io.Reader) error { if err := safeFetch(pemurl, func(body io.Reader) error {
pem, err := ioutil.ReadAll(body) pem, err := ioutil.ReadAll(body)
if nil != err { if nil != err {
return err return err
} }
pub, err = keypairs.ParsePublicKey(pem) pubd, err = keypairs.ParsePublicKey(pem)
if nil != err {
return err return err
}
return nil
}); nil != err { }); nil != err {
return nil, nil, err return nil, nil, err
} }
jwk := map[string]interface{}{} jwk := map[string]interface{}{}
pub := pubd.Key().(keypairs.PublicKeyTransitional)
body := bytes.NewBuffer(keypairs.MarshalJWKPublicKey(pub)) body := bytes.NewBuffer(keypairs.MarshalJWKPublicKey(pub))
decoder := json.NewDecoder(body) decoder := json.NewDecoder(body)
decoder.UseNumber() decoder.UseNumber()
_ = decoder.Decode(&jwk) _ = decoder.Decode(&jwk)
m := getStringMap(jwk) m := getStringMap(jwk)
m["kid"] = pemurl m["kid"] = keypairs.Thumbprint(pub)
// TODO is this just junk?
m[URLishKey] = pemurl
switch p := pub.(type) { switch pub.(type) {
case *keypairs.ECPublicKey: case *ecdsa.PublicKey:
p.KID = pemurl //p.KID = pemurl
case *keypairs.RSAPublicKey: case *rsa.PublicKey:
p.KID = pemurl //p.KID = pemurl
default: default:
return nil, nil, errors.New("impossible key type") return nil, nil, errors.New("impossible key type")
} }
@ -114,7 +131,7 @@ func PEM(pemurl string) (map[string]string, keypairs.PublicKey, error) {
} }
// Fetch retrieves a single JWK (plain, bare jwk) from a URL (off-spec) // Fetch retrieves a single JWK (plain, bare jwk) from a URL (off-spec)
func Fetch(url string) (map[string]string, keypairs.PublicKey, error) { func Fetch(url string) (map[string]string, keypairs.PublicKeyDeprecated, error) {
var m map[string]interface{} var m map[string]interface{}
if err := safeFetch(url, func(body io.Reader) error { if err := safeFetch(url, func(body io.Reader) error {
decoder := json.NewDecoder(body) decoder := json.NewDecoder(body)

View File

@ -64,8 +64,8 @@ type PublicKeyTransitional interface {
Equal(x crypto.PublicKey) bool Equal(x crypto.PublicKey) bool
} }
// PublicKey thinly veils crypto.PublicKey for type safety // PublicKeyDeprecated thinly veils crypto.PublicKey for type safety
type PublicKey interface { type PublicKeyDeprecated interface {
crypto.PublicKey crypto.PublicKey
//Equal(x crypto.PublicKey) bool //Equal(x crypto.PublicKey) bool
//Thumbprint() string //Thumbprint() string
@ -149,13 +149,13 @@ func (p *RSAPublicKey) ExpiresAt() time.Time {
} }
// NewPublicKey wraps a crypto.PublicKey to make it typesafe. // NewPublicKey wraps a crypto.PublicKey to make it typesafe.
func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey { func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKeyDeprecated {
_, ok := pub.(PublicKeyTransitional) _, ok := pub.(PublicKeyTransitional)
if !ok { if !ok {
panic("Developer Error: not a crypto.PublicKey") panic("Developer Error: not a crypto.PublicKey")
} }
var k PublicKey var k PublicKeyDeprecated
switch p := pub.(type) { switch p := pub.(type) {
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
eckey := &ECPublicKey{ eckey := &ECPublicKey{
@ -186,9 +186,9 @@ func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey {
// MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration, // MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration,
// making it suitable for use as an OIDC public key. // making it suitable for use as an OIDC public key.
func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte { func MarshalJWKPublicKey(key PublicKeyTransitional, exp ...time.Time) []byte {
// thumbprint keys are alphabetically sorted and only include the necessary public parts // thumbprint keys are alphabetically sorted and only include the necessary public parts
switch k := key.Key().(type) { switch k := key.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
return MarshalRSAPublicKey(k, exp...) return MarshalRSAPublicKey(k, exp...)
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
@ -206,7 +206,7 @@ func Thumbprint(pub PublicKeyTransitional) string {
} }
// ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint // ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint
func ThumbprintPublicKey(pub PublicKey) string { func ThumbprintPublicKey(pub PublicKeyDeprecated) string {
return ThumbprintUntypedPublicKey(pub.Key().(PublicKeyTransitional)) return ThumbprintUntypedPublicKey(pub.Key().(PublicKeyTransitional))
} }
@ -214,7 +214,7 @@ func ThumbprintPublicKey(pub PublicKey) string {
// (but will still panic, to help you discover bugs in development rather than production). // (but will still panic, to help you discover bugs in development rather than production).
func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string { func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string {
switch p := pub.(type) { switch p := pub.(type) {
case PublicKey: case PublicKeyDeprecated:
return ThumbprintUntypedPublicKey(p.Key().(PublicKeyTransitional)) return ThumbprintUntypedPublicKey(p.Key().(PublicKeyTransitional))
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
return ThumbprintECPublicKey(p) return ThumbprintECPublicKey(p)
@ -379,7 +379,7 @@ func getPEMBytes(block []byte) ([][]byte, error) {
// ParsePublicKey will try to parse the bytes you give it // ParsePublicKey will try to parse the bytes you give it
// in any of the supported formats: PEM, DER, PKIX/SPKI, PKCS1, x509 Certificate, and JWK // in any of the supported formats: PEM, DER, PKIX/SPKI, PKCS1, x509 Certificate, and JWK
func ParsePublicKey(block []byte) (PublicKey, error) { func ParsePublicKey(block []byte) (PublicKeyDeprecated, error) {
blocks, err := getPEMBytes(block) blocks, err := getPEMBytes(block)
if nil != err { if nil != err {
return nil, ErrParsePublicKey return nil, ErrParsePublicKey
@ -406,11 +406,11 @@ func ParsePublicKey(block []byte) (PublicKey, error) {
} }
// ParsePublicKeyString calls ParsePublicKey([]byte(key)) for all you lazy folk. // ParsePublicKeyString calls ParsePublicKey([]byte(key)) for all you lazy folk.
func ParsePublicKeyString(block string) (PublicKey, error) { func ParsePublicKeyString(block string) (PublicKeyDeprecated, error) {
return ParsePublicKey([]byte(block)) return ParsePublicKey([]byte(block))
} }
func parsePublicKey(der []byte) (PublicKey, error) { func parsePublicKey(der []byte) (PublicKeyDeprecated, error) {
cert, err := x509.ParseCertificate(der) cert, err := x509.ParseCertificate(der)
if nil == err { if nil == err {
switch k := cert.PublicKey.(type) { switch k := cert.PublicKey.(type) {
@ -456,7 +456,7 @@ func parsePublicKey(der []byte) (PublicKey, error) {
} }
// NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON) // NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON)
func NewJWKPublicKey(m map[string]string) (PublicKey, error) { func NewJWKPublicKey(m map[string]string) (PublicKeyDeprecated, error) {
switch m["kty"] { switch m["kty"] {
case "RSA": case "RSA":
return parseRSAPublicKey(m) return parseRSAPublicKey(m)
@ -468,7 +468,7 @@ func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
} }
// ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message // ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func ParseJWKPublicKey(b []byte) (PublicKey, error) { func ParseJWKPublicKey(b []byte) (PublicKeyDeprecated, error) {
// RSA and EC have "d" as a private part // RSA and EC have "d" as a private part
if bytes.Contains(b, []byte(`"d"`)) { if bytes.Contains(b, []byte(`"d"`)) {
return nil, ErrUnexpectedPrivateKey return nil, ErrUnexpectedPrivateKey
@ -477,7 +477,7 @@ func ParseJWKPublicKey(b []byte) (PublicKey, error) {
} }
// ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk. // ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.
func ParseJWKPublicKeyString(s string) (PublicKey, error) { func ParseJWKPublicKeyString(s string) (PublicKeyDeprecated, error) {
if strings.Contains(s, `"d"`) { if strings.Contains(s, `"d"`) {
return nil, ErrUnexpectedPrivateKey return nil, ErrUnexpectedPrivateKey
} }
@ -485,7 +485,7 @@ func ParseJWKPublicKeyString(s string) (PublicKey, error) {
} }
// DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message // DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) { func DecodeJWKPublicKey(r io.Reader) (PublicKeyDeprecated, error) {
m := make(map[string]string) m := make(map[string]string)
if err := json.NewDecoder(r).Decode(&m); nil != err { if err := json.NewDecoder(r).Decode(&m); nil != err {
return nil, err return nil, err
@ -497,7 +497,7 @@ func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) {
} }
// the underpinnings of the parser as used by the typesafe wrappers // the underpinnings of the parser as used by the typesafe wrappers
func newJWKPublicKey(data interface{}) (PublicKey, error) { func newJWKPublicKey(data interface{}) (PublicKeyDeprecated, error) {
var m map[string]string var m map[string]string
switch d := data.(type) { switch d := data.(type) {

View File

@ -39,7 +39,7 @@ var never = time.Time{}
// Middleware holds your public keys and has http handler methods for OIDC and Auth0 JWKs // Middleware holds your public keys and has http handler methods for OIDC and Auth0 JWKs
type Middleware struct { type Middleware struct {
BaseURL *url.URL BaseURL *url.URL
Keys []keypairs.PublicKey Keys []keypairs.PublicKeyDeprecated
ExpiresIn time.Duration ExpiresIn time.Duration
} }
@ -148,7 +148,7 @@ func (m *Middleware) Auth0PEM(w http.ResponseWriter, r *http.Request) {
} }
} }
func marshalJWKs(keys []keypairs.PublicKey, exp2 time.Time) []string { func marshalJWKs(keys []keypairs.PublicKeyDeprecated, exp2 time.Time) []string {
jwks := make([]string, 0, 1) jwks := make([]string, 0, 1)
for i := range keys { for i := range keys {
@ -163,7 +163,7 @@ func marshalJWKs(keys []keypairs.PublicKey, exp2 time.Time) []string {
// Note that you don't have to embed `iss` in the JWK because the client // Note that you don't have to embed `iss` in the JWK because the client
// already has that info by virtue of getting to it in the first place. // already has that info by virtue of getting to it in the first place.
jwk := string(keypairs.MarshalJWKPublicKey(key, exp)) jwk := string(keypairs.MarshalJWKPublicKey(key.Key().(keypairs.PublicKeyTransitional), exp))
jwks = append(jwks, jwk) jwks = append(jwks, jwk)
} }

View File

@ -18,7 +18,7 @@ import (
func TestServeKeys(t *testing.T) { func TestServeKeys(t *testing.T) {
eckey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) eckey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
pubs := []keypairs.PublicKey{ pubs := []keypairs.PublicKeyDeprecated{
keypairs.NewPublicKey(eckey.Public()), keypairs.NewPublicKey(eckey.Public()),
} }
@ -42,8 +42,9 @@ func TestServeKeys(t *testing.T) {
go func() { go func() {
time.Sleep(15 * time.Second) time.Sleep(15 * time.Second)
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
h.Shutdown(ctx) h.Shutdown(ctx)
cancel()
}() }()
m := map[string]string{} m := map[string]string{}

View File

@ -26,7 +26,7 @@ func SignClaims(privkey PrivateKey, header Object, claims Object) (*JWS, error)
//delete(header, "_seed") //delete(header, "_seed")
} }
protected, header, err := headerToProtected(NewPublicKey(privkey.Public()), header) protected, header, err := headerToProtected(privkey.Public().(PublicKeyTransitional), header)
if nil != err { if nil != err {
return nil, err return nil, err
} }
@ -56,7 +56,7 @@ func SignClaims(privkey PrivateKey, header Object, claims Object) (*JWS, error)
}, nil }, nil
} }
func headerToProtected(pub PublicKey, header Object) ([]byte, Object, error) { func headerToProtected(pub PublicKeyTransitional, header Object) ([]byte, Object, error) {
if nil == header { if nil == header {
header = Object{} header = Object{}
} }
@ -65,7 +65,7 @@ func headerToProtected(pub PublicKey, header Object) ([]byte, Object, error) {
// because that's all that's practical and well-supported. // because that's all that's practical and well-supported.
// No security theatre here. // No security theatre here.
alg := "ES256" alg := "ES256"
switch pub.Key().(type) { switch pub.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
alg = "RS256" alg = "RS256"
} }
@ -80,7 +80,7 @@ func headerToProtected(pub PublicKey, header Object) ([]byte, Object, error) {
// TODO what are the acceptable values? JWT. JWS? others? // TODO what are the acceptable values? JWT. JWS? others?
header["typ"] = "JWT" header["typ"] = "JWT"
if _, ok := header["jwk"]; !ok { if _, ok := header["jwk"]; !ok {
thumbprint := ThumbprintPublicKey(pub) thumbprint := ThumbprintPublicKey(NewPublicKey(pub))
kid, _ := header["kid"].(string) kid, _ := header["kid"].(string)
if "" != kid && thumbprint != kid { if "" != kid && thumbprint != kid {
return nil, nil, errors.New("'kid' should be the key's thumbprint") return nil, nil, errors.New("'kid' should be the key's thumbprint")

View File

@ -15,7 +15,7 @@ import (
) )
// VerifyClaims will check the signature of a parsed JWT // VerifyClaims will check the signature of a parsed JWT
func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error) { func VerifyClaims(pubkey PublicKeyTransitional, jws *JWS) (errs []error) {
kid, _ := jws.Header["kid"].(string) kid, _ := jws.Header["kid"].(string)
jwkmap, hasJWK := jws.Header["jwk"].(Object) jwkmap, hasJWK := jws.Header["jwk"].(Object)
//var jwk JWK = nil //var jwk JWK = nil
@ -27,7 +27,7 @@ func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error) {
seed = int64(seedf64) seed = int64(seedf64)
} }
var pub PublicKey = nil var pub PublicKeyTransitional = nil
if hasJWK { if hasJWK {
pub, errs = selfsignCheck(jwkmap, errs) pub, errs = selfsignCheck(jwkmap, errs)
} else { } else {
@ -72,8 +72,8 @@ func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error) {
return errs return errs
} }
func selfsignCheck(jwkmap Object, errs []error) (PublicKey, []error) { func selfsignCheck(jwkmap Object, errs []error) (PublicKeyTransitional, []error) {
var pub PublicKey = nil var pub PublicKeyDeprecated = nil
log.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk'") log.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk'")
log.Println("Security TODO: did not check jws.Claims[\"iss\"]") log.Println("Security TODO: did not check jws.Claims[\"iss\"]")
kty := jwkmap["kty"] kty := jwkmap["kty"]
@ -104,11 +104,11 @@ func selfsignCheck(jwkmap Object, errs []error) (PublicKey, []error) {
} }
} }
return pub, errs return pub.Key().(PublicKeyTransitional), errs
} }
func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (PublicKey, []error) { func pubkeyCheck(pubkey PublicKeyTransitional, kid string, opts *keyOptions, errs []error) (PublicKeyTransitional, []error) {
var pub PublicKey = nil var pub PublicKeyTransitional = nil
if "" == kid { if "" == kid {
err := errors.New("token should have 'kid' or 'jwk' in header to identify the public key") err := errors.New("token should have 'kid' or 'jwk' in header to identify the public key")
@ -130,7 +130,7 @@ func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (
return nil, errs return nil, errs
} }
privkey := newPrivateKey(opts) privkey := newPrivateKey(opts)
pub = NewPublicKey(privkey.Public()) pub = privkey.Public().(PublicKeyTransitional)
return pub, errs return pub, errs
} }
err := errors.New("no matching public key") err := errors.New("no matching public key")
@ -140,7 +140,7 @@ func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (
} }
if nil != pub && "" != kid { if nil != pub && "" != kid {
if 1 != subtle.ConstantTimeCompare([]byte(kid), []byte(Thumbprint(pub.Key().(PublicKeyTransitional)))) { if 1 != subtle.ConstantTimeCompare([]byte(kid), []byte(Thumbprint(pub))) {
err := errors.New("'kid' does not match the public key thumbprint") err := errors.New("'kid' does not match the public key thumbprint")
errs = append(errs, err) errs = append(errs, err)
} }
@ -149,9 +149,9 @@ func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (
} }
// Verify will check the signature of a hash // Verify will check the signature of a hash
func Verify(pubkey PublicKey, hash []byte, sig []byte) bool { func Verify(pubkey PublicKeyTransitional, hash []byte, sig []byte) bool {
switch pub := pubkey.Key().(type) { switch pub := pubkey.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
//log.Printf("RSA VERIFY") //log.Printf("RSA VERIFY")
// TODO Size(key) to detect key size ? // TODO Size(key) to detect key size ?