jwt: began basic JWT. crypto: moved SigningMethod into crypto
diff --git a/jws/ecdsa.go b/crypto/ecdsa.go similarity index 99% rename from jws/ecdsa.go rename to crypto/ecdsa.go index ce46b47..c518c9d 100644 --- a/jws/ecdsa.go +++ b/crypto/ecdsa.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto"
diff --git a/jws/ecdsa_test.go b/crypto/ecdsa_test.go similarity index 99% rename from jws/ecdsa_test.go rename to crypto/ecdsa_test.go index 30a0f60..e557d1a 100644 --- a/jws/ecdsa_test.go +++ b/crypto/ecdsa_test.go
@@ -1,4 +1,4 @@ -package jws +package crypto // import ( // "crypto/ecdsa"
diff --git a/jws/ecdsa_utils.go b/crypto/ecdsa_utils.go similarity index 98% rename from jws/ecdsa_utils.go rename to crypto/ecdsa_utils.go index e08a346..4bd75d2 100644 --- a/jws/ecdsa_utils.go +++ b/crypto/ecdsa_utils.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto/ecdsa"
diff --git a/crypto/errors.go b/crypto/errors.go new file mode 100644 index 0000000..34fbd25 --- /dev/null +++ b/crypto/errors.go
@@ -0,0 +1,9 @@ +package crypto + +import "errors" + +var ( + // ErrInvalidKey means the key argument passed to SigningMethod.Verify + // was not the correct type. + ErrInvalidKey = errors.New("key is invalid") +)
diff --git a/jws/hmac.go b/crypto/hmac.go similarity index 98% rename from jws/hmac.go rename to crypto/hmac.go index 1d25d72..a9afb1f 100644 --- a/jws/hmac.go +++ b/crypto/hmac.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto"
diff --git a/jws/hmac_test.go b/crypto/hmac_test.go similarity index 99% rename from jws/hmac_test.go rename to crypto/hmac_test.go index 86b357e..ff46861 100644 --- a/jws/hmac_test.go +++ b/crypto/hmac_test.go
@@ -1,4 +1,4 @@ -package jws +package crypto // import ( // "github.com/dgrijalva/jwt-go"
diff --git a/jws/none.go b/crypto/none.go similarity index 93% rename from jws/none.go rename to crypto/none.go index 5d0a4bf..2b27af4 100644 --- a/jws/none.go +++ b/crypto/none.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto" @@ -7,10 +7,7 @@ "io" ) -func init() { - crypto.RegisterHash(crypto.Hash(0), h) - RegisterSigningMethod(Unsecured) -} +func init() { crypto.RegisterHash(crypto.Hash(0), h) } // h is passed to crypto.RegisterHash. func h() hash.Hash { return &f{Writer: nil} }
diff --git a/jws/rsa.go b/crypto/rsa.go similarity index 98% rename from jws/rsa.go rename to crypto/rsa.go index 9174478..80596df 100644 --- a/jws/rsa.go +++ b/crypto/rsa.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto"
diff --git a/jws/rsa_pss.go b/crypto/rsa_pss.go similarity index 98% rename from jws/rsa_pss.go rename to crypto/rsa_pss.go index 81330b8..3847ae2 100644 --- a/jws/rsa_pss.go +++ b/crypto/rsa_pss.go
@@ -1,6 +1,6 @@ // +build go1.4 -package jws +package crypto import ( "crypto"
diff --git a/jws/rsa_pss_test.go b/crypto/rsa_pss_test.go similarity index 99% rename from jws/rsa_pss_test.go rename to crypto/rsa_pss_test.go index c45ecf7..c88f949 100644 --- a/jws/rsa_pss_test.go +++ b/crypto/rsa_pss_test.go
@@ -1,6 +1,6 @@ // +build go1.4 -package jws_test +package crypto_test // import ( // "crypto/rsa"
diff --git a/jws/rsa_test.go b/crypto/rsa_test.go similarity index 99% rename from jws/rsa_test.go rename to crypto/rsa_test.go index 7eb9753..c594605 100644 --- a/jws/rsa_test.go +++ b/crypto/rsa_test.go
@@ -1,4 +1,4 @@ -package jws_test +package crypto_test // import ( // "github.com/dgrijalva/jwt-go"
diff --git a/jws/rsa_utils.go b/crypto/rsa_utils.go similarity index 98% rename from jws/rsa_utils.go rename to crypto/rsa_utils.go index d8a1a75..350c394 100644 --- a/jws/rsa_utils.go +++ b/crypto/rsa_utils.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "crypto/rsa"
diff --git a/jws/signature.go b/crypto/signature.go similarity index 97% rename from jws/signature.go rename to crypto/signature.go index 6fbdc78..37571f9 100644 --- a/jws/signature.go +++ b/crypto/signature.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "encoding/json"
diff --git a/jws/signature_test.go b/crypto/signature_test.go similarity index 95% rename from jws/signature_test.go rename to crypto/signature_test.go index 74714be..7601065 100644 --- a/jws/signature_test.go +++ b/crypto/signature_test.go
@@ -1,4 +1,4 @@ -package jws +package crypto import ( "bytes"
diff --git a/crypto/signing_method.go b/crypto/signing_method.go new file mode 100644 index 0000000..998ac59 --- /dev/null +++ b/crypto/signing_method.go
@@ -0,0 +1,24 @@ +package crypto + +// SigningMethod is an interface that provides a way to sign JWS tokens. +import "crypto" + +type SigningMethod interface { + // Alg describes the signing algorithm, and is used to uniquely + // describe the specific crypto.SigningMethod. + Alg() string + + // Verify accepts the raw content, the signature, and the key used + // to sign the raw content, and returns any errors found while validating + // the signature and content. + Verify(raw []byte, sig Signature, key interface{}) error + + // Sign returns a Signature for the raw bytes, as well as any errors + // that occurred during the signing. + Sign(raw []byte, key interface{}) (Signature, error) + + // Used to cause quick panics when a crypto.SigningMethod whose form of hashing + // isn't linked in the binary when you register a crypto.SigningMethod. + // To spoof this, see "crypto.SigningMethodNone". + Hasher() crypto.Hash +}
diff --git a/crypto/stubs_test.go b/crypto/stubs_test.go new file mode 100644 index 0000000..96ec4fd --- /dev/null +++ b/crypto/stubs_test.go
@@ -0,0 +1,22 @@ +package crypto + +import ( + "fmt" + "testing" +) + +func Error(t *testing.T, want, got interface{}) { + format := "\nWanted: %s\nGot: %s" + + switch want.(type) { + case []byte, string, nil: + default: + format = fmt.Sprintf(format, "%v", "%v") + } + + t.Errorf(format, want, got) +} + +func ErrorTypes(t *testing.T, want, got interface{}) { + t.Errorf("\nWanted: %T\nGot: %T", want, got) +}
diff --git a/jws/errors.go b/jws/errors.go index 2d48368..6f6de54 100644 --- a/jws/errors.go +++ b/jws/errors.go
@@ -3,9 +3,6 @@ import "errors" var ( - // ErrInvalidKey means the key argument passed to SigningMethod.Verify - // was not the correct type. - ErrInvalidKey = errors.New("key is invalid") // ErrNotEnoughMethods is returned if New was called _or_ the Flat/Compact // methods were called with 0 SigningMethods. @@ -49,4 +46,10 @@ // reasons. For example, if there aren't any signatures/payloads/headers // to actually validate. ErrCannotValidate = errors.New("cannot validate") + + // ErrIsNotJWT means the given JWS is not a JWT. + ErrIsNotJWT = errors.New("JWS is not a JWT") + + // ErrHoldsJWE means the given JWS holds a JWE inside its payload. + ErrHoldsJWE = errors.New("JWS holds JWE") )
diff --git a/jws/jws.go b/jws/jws.go index 4147d37..2a65f25 100644 --- a/jws/jws.go +++ b/jws/jws.go
@@ -6,6 +6,7 @@ "sort" "github.com/SermoDigital/jose" + "github.com/SermoDigital/jose/crypto" ) // JWS represents a specific JWS. @@ -15,8 +16,13 @@ clean bool sb []sigHead + + isJWT bool } +// Payload returns the JWS' payload. +func (j *JWS) Payload() interface{} { return j.payload.v } + // sigHead represents the 'signatures' member of the JWS' "general" // serialization form per // https://tools.ietf.org/html/rfc7515#section-7.2.1 @@ -24,15 +30,15 @@ // It's embedded inside the "flat" structure in order to properly // create the "flat" JWS. type sigHead struct { - Protected rawBase64 `json:"protected,omitempty"` - Unprotected rawBase64 `json:"header,omitempty"` - Signature Signature `json:"signature"` + Protected rawBase64 `json:"protected,omitempty"` + Unprotected rawBase64 `json:"header,omitempty"` + Signature crypto.Signature `json:"signature"` - protected jose.Protected `json:"-"` - unprotected jose.Header `json:"-"` - clean bool `json:"-"` + protected jose.Protected + unprotected jose.Header + clean bool - method SigningMethod + method crypto.SigningMethod } func (s *sigHead) unmarshal() error { @@ -45,8 +51,8 @@ return nil } -// New creates a new JWS with the provided SigningMethods. -func New(content interface{}, methods ...SigningMethod) *JWS { +// New creates a new JWS with the provided crypto.SigningMethods. +func New(content interface{}, methods ...crypto.SigningMethod) *JWS { sb := make([]sigHead, len(methods)) for i := range methods { sb[i] = sigHead{ @@ -218,6 +224,10 @@ // For information on the json.Unmarshaler parameter, see Parse. func ParseCompact(encoded []byte, u ...json.Unmarshaler) (*JWS, error) { + // This section loosely follows + // https://tools.ietf.org/html/rfc7519#section-7.2 + // because it's used to parse _both_ JWS and JWTs. + parts := bytes.Split(encoded, []byte{'.'}) if len(parts) != 3 { return nil, ErrNotCompact @@ -252,6 +262,11 @@ return nil, err } + // https://tools.ietf.org/html/rfc7519#section-7.2.8 + cty, ok := p.Get("cty").(string) + if ok && cty == "JWT" { + return &j, ErrHoldsJWE + } return &j, nil } @@ -279,6 +294,8 @@ return nil } +// Any means any of the JWS signatures need to validate. +// Refer to ValidateMulti for more information. const Any int = -1 // ValidateMulti validates the current JWS as-is. Since it's meant to be @@ -286,14 +303,16 @@ // internal parsing like the Sign, Flat, Compact, or General methods do. // idx represents which signatures need to validate // in order for the JWS to be considered valid. -// Use the constant `Any` (-1) if _any_ should validate the JWS. Otherwise, +// Use the constant `Any` (-1) if *any* should validate the JWS. Otherwise, // use the indexes of the signatures that need to validate in order // for the JWS to be considered valid. -// Note: if idx is omitted it defaults to requiring _all_ -// signatures validate, and the JWS spec required _at least_ one -// signature to validate in order for the JWS to be considered -// valid. -func (j *JWS) ValidateMulti(keys []interface{}, methods []SigningMethod, idx ...int) error { +// +// Notes: +// 1.) If idx is omitted it defaults to requiring *all* +// signatures validate +// 2.) The JWS spec requires *at least* one +// signature to validate in order for the JWS to be considered valid. +func (j *JWS) ValidateMulti(keys []interface{}, methods []crypto.SigningMethod, idx ...int) error { if len(j.sb) != len(methods) { return ErrNotEnoughMethods @@ -333,14 +352,17 @@ // Validate validates the current JWS as-is. Refer to ValidateMulti // for more information. -func (j *JWS) Validate(key interface{}, method SigningMethod) error { +func (j *JWS) Validate(key interface{}, method crypto.SigningMethod) error { if len(j.sb) < 1 { return ErrCannotValidate } + if j.isJWT { + return j.validateJWT(key, method) + } return j.sb[0].validate(j.plcache, key, method) } -func (s *sigHead) validate(pl []byte, key interface{}, method SigningMethod) error { +func (s *sigHead) validate(pl []byte, key interface{}, method crypto.SigningMethod) error { if s.method != method { return ErrMismatchedAlgorithms }
diff --git a/jws/jws_serialize.go b/jws/jws_serialize.go index 46ecd38..33369dd 100644 --- a/jws/jws_serialize.go +++ b/jws/jws_serialize.go
@@ -27,8 +27,8 @@ // https://tools.ietf.org/html/rfc7515#section-7.2.1 // // If only one key is passed it's used for all the provided -// SigningMethods. Otherwise, len(keys) must equal the number -// of SigningMethods added. +// crypto.SigningMethods. Otherwise, len(keys) must equal the number +// of crypto.SigningMethods added. func (j *JWS) General(keys ...interface{}) ([]byte, error) { if err := j.sign(keys...); err != nil { return nil, err
diff --git a/jws/jws_serialize_test.go b/jws/jws_serialize_test.go index 124a3b4..6472fe3 100644 --- a/jws/jws_serialize_test.go +++ b/jws/jws_serialize_test.go
@@ -6,6 +6,7 @@ "testing" "github.com/SermoDigital/jose" + "github.com/SermoDigital/jose/crypto" ) var dataRaw = struct { @@ -44,7 +45,7 @@ } func TestGeneralIntegrity(t *testing.T) { - j := New(dataRaw, SigningMethodRS512) + j := New(dataRaw, crypto.SigningMethodRS512) b, err := j.General(rsaPriv) if err != nil { t.Error(err) @@ -70,7 +71,7 @@ } func TestFlatIntegrity(t *testing.T) { - j := New(dataRaw, SigningMethodRS512) + j := New(dataRaw, crypto.SigningMethodRS512) b, err := j.Flat(rsaPriv) if err != nil { t.Error(err) @@ -96,7 +97,7 @@ } func TestCompactIntegrity(t *testing.T) { - j := New(dataRaw, SigningMethodRS512) + j := New(dataRaw, crypto.SigningMethodRS512) b, err := j.Compact(rsaPriv) if err != nil { t.Error(err)
diff --git a/jws/jws_test.go b/jws/jws_test.go index ebd7cf0..6c8355f 100644 --- a/jws/jws_test.go +++ b/jws/jws_test.go
@@ -5,6 +5,8 @@ "encoding/base64" "encoding/json" "testing" + + "github.com/SermoDigital/jose/crypto" ) type easy []byte @@ -29,7 +31,7 @@ var easyData = easy("easy data!") func TestParseWithUnmarshaler(t *testing.T) { - j := New(easyData, SigningMethodRS512) + j := New(easyData, crypto.SigningMethodRS512) b, err := j.Flat(rsaPriv) if err != nil { t.Error(err) @@ -47,7 +49,7 @@ } func TestParseCompact(t *testing.T) { - j := New(easyData, SigningMethodRS512) + j := New(easyData, crypto.SigningMethodRS512) b, err := j.Compact(rsaPriv) if err != nil { t.Error(err) @@ -69,7 +71,7 @@ } func TestParseGeneral(t *testing.T) { - sm := []SigningMethod{SigningMethodRS512, SigningMethodPS384, SigningMethodPS256} + sm := []crypto.SigningMethod{crypto.SigningMethodRS512, crypto.SigningMethodPS384, crypto.SigningMethodPS256} j := New(easyData, sm...) b, err := j.General(rsaPriv) if err != nil { @@ -90,7 +92,7 @@ } func TestValidateMulti(t *testing.T) { - sm := []SigningMethod{SigningMethodRS512, SigningMethodPS384, SigningMethodPS256} + sm := []crypto.SigningMethod{crypto.SigningMethodRS512, crypto.SigningMethodPS384, crypto.SigningMethodPS256} j := New(easyData, sm...) b, err := j.General(rsaPriv) if err != nil { @@ -109,7 +111,7 @@ } func TestValidateMultiMismatchedAlgs(t *testing.T) { - sm := []SigningMethod{SigningMethodRS256, SigningMethodPS384, SigningMethodPS512} + sm := []crypto.SigningMethod{crypto.SigningMethodRS256, crypto.SigningMethodPS384, crypto.SigningMethodPS512} j := New(easyData, sm...) b, err := j.General(rsaPriv) if err != nil { @@ -122,7 +124,7 @@ } // Shuffle it. - sm = []SigningMethod{SigningMethodRS512, SigningMethodPS256, SigningMethodPS384} + sm = []crypto.SigningMethod{crypto.SigningMethodRS512, crypto.SigningMethodPS256, crypto.SigningMethodPS384} keys := []interface{}{rsaPub, rsaPub, rsaPub} if err := j2.ValidateMulti(keys, sm, Any); err == nil { @@ -131,7 +133,7 @@ } func TestValidateMultiNotEnoughMethods(t *testing.T) { - sm := []SigningMethod{SigningMethodRS256, SigningMethodPS384, SigningMethodPS512} + sm := []crypto.SigningMethod{crypto.SigningMethodRS256, crypto.SigningMethodPS384, crypto.SigningMethodPS512} j := New(easyData, sm...) b, err := j.General(rsaPriv) if err != nil { @@ -152,7 +154,7 @@ } func TestValidateMultiNotEnoughKeys(t *testing.T) { - sm := []SigningMethod{SigningMethodRS256, SigningMethodPS384, SigningMethodPS512} + sm := []crypto.SigningMethod{crypto.SigningMethodRS256, crypto.SigningMethodPS384, crypto.SigningMethodPS512} j := New(easyData, sm...) b, err := j.General(rsaPriv) if err != nil { @@ -171,7 +173,7 @@ } func TestValidate(t *testing.T) { - j := New(easyData, SigningMethodPS512) + j := New(easyData, crypto.SigningMethodPS512) b, err := j.Flat(rsaPriv) if err != nil { t.Error(err) @@ -182,7 +184,7 @@ t.Error(err) } - if err := j2.Validate(rsaPub, SigningMethodPS512); err != nil { + if err := j2.Validate(rsaPub, crypto.SigningMethodPS512); err != nil { t.Error(err) } }
diff --git a/jws/jwt.go b/jws/jwt.go new file mode 100644 index 0000000..ce2e985 --- /dev/null +++ b/jws/jwt.go
@@ -0,0 +1,48 @@ +package jws + +import ( + "github.com/SermoDigital/jose/crypto" + "github.com/SermoDigital/jose/jwt" +) + +// Claims represents a set of JOSE Claims. +type Claims jwt.Claims + +// NewJWT creates a new JWT with the given claims. +func NewJWT(claims Claims, method crypto.SigningMethod) jwt.JWT { + j := New(claims, method) + j.isJWT = true + return j +} + +// Serialize helps implements jwt.JWT. +func (j *JWS) Serialize(key interface{}) ([]byte, error) { + if j.isJWT { + return j.Compact(key) + } + return nil, ErrIsNotJWT +} + +// Claims helps implements jwt.JWT. +func (j *JWS) Claims() jwt.Claims { + if j.isJWT { + if c, ok := j.payload.v.(Claims); ok { + return jwt.Claims(c) + } + } + return nil +} + +// ParseJWT parses a serialized JWT into a physical JWT. +func ParseJWT(encoded []byte) (JWT, error) { + return ParseCompact(encoded) +} + +// IsJWT returns true if the JWS is a JWT. +func (j *JWS) IsJWT() bool { return j.isJWT } + +func (j *JWS) validateJWT(key interface{}, m crypto.SigningMethod) error { + return nil +} + +var _ jwt.JWT = (*JWS)(nil)
diff --git a/jws/signing_methods.go b/jws/signing_methods.go index bc21d69..1b6665f 100644 --- a/jws/signing_methods.go +++ b/jws/signing_methods.go
@@ -1,56 +1,38 @@ package jws import ( - "crypto" "sync" + + "github.com/SermoDigital/jose/crypto" ) var ( mu = &sync.RWMutex{} - signingMethods = map[string]SigningMethod{ - SigningMethodES256.Alg(): SigningMethodES256, - SigningMethodES384.Alg(): SigningMethodES384, - SigningMethodES512.Alg(): SigningMethodES512, + signingMethods = map[string]crypto.SigningMethod{ + crypto.SigningMethodES256.Alg(): crypto.SigningMethodES256, + crypto.SigningMethodES384.Alg(): crypto.SigningMethodES384, + crypto.SigningMethodES512.Alg(): crypto.SigningMethodES512, - SigningMethodPS256.Alg(): SigningMethodPS256, - SigningMethodPS384.Alg(): SigningMethodPS384, - SigningMethodPS512.Alg(): SigningMethodPS512, + crypto.SigningMethodPS256.Alg(): crypto.SigningMethodPS256, + crypto.SigningMethodPS384.Alg(): crypto.SigningMethodPS384, + crypto.SigningMethodPS512.Alg(): crypto.SigningMethodPS512, - SigningMethodRS256.Alg(): SigningMethodRS256, - SigningMethodRS384.Alg(): SigningMethodRS384, - SigningMethodRS512.Alg(): SigningMethodRS512, + crypto.SigningMethodRS256.Alg(): crypto.SigningMethodRS256, + crypto.SigningMethodRS384.Alg(): crypto.SigningMethodRS384, + crypto.SigningMethodRS512.Alg(): crypto.SigningMethodRS512, - SigningMethodHS256.Alg(): SigningMethodHS256, - SigningMethodHS384.Alg(): SigningMethodHS384, - SigningMethodHS512.Alg(): SigningMethodHS512, + crypto.SigningMethodHS256.Alg(): crypto.SigningMethodHS256, + crypto.SigningMethodHS384.Alg(): crypto.SigningMethodHS384, + crypto.SigningMethodHS512.Alg(): crypto.SigningMethodHS512, + + crypto.Unsecured.Alg(): crypto.Unsecured, } ) -// SigningMethod is an interface that provides a way to sign JWS tokens. -type SigningMethod interface { - // Alg describes the signing algorithm, and is used to uniquely - // describe the specific SigningMethod. - Alg() string - - // Verify accepts the raw content, the signature, and the key used - // to sign the raw content, and returns any errors found while validating - // the signature and content. - Verify(raw []byte, sig Signature, key interface{}) error - - // Sign returns a Signature for the raw bytes, as well as any errors - // that occurred during the signing. - Sign(raw []byte, key interface{}) (Signature, error) - - // Used to cause quick panics when a SigningMethod whose form of hashing - // isn't linked in the binary when you register a SigningMethod. - // To spoof this, see "SigningMethodNone". - Hasher() crypto.Hash -} - -// RegisterSigningMethod registers the SigningMethod in the global map. +// RegisterSigningMethod registers the crypto.SigningMethod in the global map. // This is typically done inside the caller's init function. -func RegisterSigningMethod(sm SigningMethod) { +func RegisterSigningMethod(sm crypto.SigningMethod) { if GetSigningMethod(sm.Alg()) != nil { panic("jose/jws: cannot duplicate signing methods") } @@ -64,15 +46,15 @@ mu.Unlock() } -// RemoveSigningMethod removes the SigningMethod from the global map. -func RemoveSigningMethod(sm SigningMethod) { +// RemoveSigningMethod removes the crypto.SigningMethod from the global map. +func RemoveSigningMethod(sm crypto.SigningMethod) { mu.Lock() delete(signingMethods, sm.Alg()) mu.Unlock() } -// GetSigningMethod retrieves a SigningMethod from the global map. -func GetSigningMethod(alg string) SigningMethod { +// GetSigningMethod retrieves a crypto.SigningMethod from the global map. +func GetSigningMethod(alg string) crypto.SigningMethod { mu.RLock() defer mu.RUnlock() return signingMethods[alg]
diff --git a/jws/signing_methods_test.go b/jws/signing_methods_test.go index 59d52c2..017d482 100644 --- a/jws/signing_methods_test.go +++ b/jws/signing_methods_test.go
@@ -5,6 +5,8 @@ "hash" "io" "testing" + + c "github.com/SermoDigital/jose/crypto" ) func init() { crypto.RegisterHash(crypto.Hash(0), HH) } @@ -29,11 +31,11 @@ Hash crypto.Hash } -func (m *TestSigningMethod) Verify(_ []byte, _ Signature, _ interface{}) error { +func (m *TestSigningMethod) Verify(_ []byte, _ c.Signature, _ interface{}) error { return nil } -func (m *TestSigningMethod) Sign(_ []byte, _ interface{}) (Signature, error) { +func (m *TestSigningMethod) Sign(_ []byte, _ interface{}) (c.Signature, error) { return nil, nil }
diff --git a/jws/stubs_test.go b/jws/stubs_test.go index 0b9e9b2..513c6ef 100644 --- a/jws/stubs_test.go +++ b/jws/stubs_test.go
@@ -9,6 +9,8 @@ "io/ioutil" "path/filepath" "testing" + + "github.com/SermoDigital/jose/crypto" ) func Error(t *testing.T, want, got interface{}) { @@ -66,7 +68,7 @@ if err != nil { panic(err) } - ec256Priv, err = ParseECPrivateKeyFromPEM(ecData) + ec256Priv, err = crypto.ParseECPrivateKeyFromPEM(ecData) if err != nil { panic(err) } @@ -74,7 +76,7 @@ if err != nil { panic(err) } - ec256Pub, err = ParseECPublicKeyFromPEM(ecData) + ec256Pub, err = crypto.ParseECPublicKeyFromPEM(ecData) if err != nil { panic(err) } @@ -82,7 +84,7 @@ if err != nil { panic(err) } - ec384Priv, err = ParseECPrivateKeyFromPEM(ecData) + ec384Priv, err = crypto.ParseECPrivateKeyFromPEM(ecData) if err != nil { panic(err) } @@ -90,7 +92,7 @@ if err != nil { panic(err) } - ec384Pub, err = ParseECPublicKeyFromPEM(ecData) + ec384Pub, err = crypto.ParseECPublicKeyFromPEM(ecData) if err != nil { panic(err) } @@ -98,7 +100,7 @@ if err != nil { panic(err) } - ec512Priv, err = ParseECPrivateKeyFromPEM(ecData) + ec512Priv, err = crypto.ParseECPrivateKeyFromPEM(ecData) if err != nil { panic(err) } @@ -106,7 +108,7 @@ if err != nil { panic(err) } - ec512Pub, err = ParseECPublicKeyFromPEM(ecData) + ec512Pub, err = crypto.ParseECPublicKeyFromPEM(ecData) if err != nil { panic(err) }
diff --git a/jwt/claims.go b/jwt/claims.go new file mode 100644 index 0000000..46076d9 --- /dev/null +++ b/jwt/claims.go
@@ -0,0 +1,76 @@ +package jwt + +import ( + "encoding/json" + + "github.com/SermoDigital/jose" +) + +// Claims implements a JOSE Claim set with the addition of some helper +// methods, similar to net/url.Values. +type Claims map[string]interface{} + +// Get retrieves the value corresponding with key from the Claims. +func (c Claims) Get(key string) interface{} { + if c == nil { + return nil + } + return c[key] +} + +// Set sets Claims[key] = val. It'll overwrite without warning. +func (c Claims) Set(key string, val interface{}) { + c[key] = val +} + +// Del removes the value that corresponds with key from the Claims. +func (c Claims) Del(key string) { + delete(c, key) +} + +// Has returns true if a value for the given key exists inside the Claims. +func (c Claims) Has(key string) bool { + _, ok := c[key] + return ok +} + +// MarshalJSON implements json.Marshaler for Claims. +func (c Claims) MarshalJSON() ([]byte, error) { + if c == nil || len(c) == 0 { + return nil, nil + } + b, err := json.Marshal(map[string]interface{}(c)) + if err != nil { + return nil, err + } + return jose.EncodeEscape(b), nil +} + +// Base64 implements the Encoder interface. +func (c Claims) Base64() ([]byte, error) { + return c.MarshalJSON() +} + +// UnmarshalJSON implements json.Unmarshaler for Claims. +func (c *Claims) UnmarshalJSON(b []byte) error { + if b == nil { + return nil + } + + b, err := jose.DecodeEscaped(b) + if err != nil { + return err + } + + // Since json.Unmarshal calls UnmarshalJSON, + // calling json.Unmarshal on *p would be infinitely recursive + // A temp variable is needed because &map[string]interface{}(*p) is + // invalid Go. + + tmp := map[string]interface{}(*c) + if err = json.Unmarshal(b, &tmp); err != nil { + return err + } + *c = Claims(tmp) + return nil +}
diff --git a/jwt/claims_test.go b/jwt/claims_test.go new file mode 100644 index 0000000..e22cb07 --- /dev/null +++ b/jwt/claims_test.go
@@ -0,0 +1 @@ +package jwt
diff --git a/jwt/jwt.go b/jwt/jwt.go new file mode 100644 index 0000000..bd09ec0 --- /dev/null +++ b/jwt/jwt.go
@@ -0,0 +1,16 @@ +package jwt + +import "github.com/SermoDigital/jose/crypto" + +type JWT interface { + // Claims returns the set of Claims. + Claims() Claims + + // Validate returns an error describing any issues found while + // validating the JWT. + Validate(key interface{}, method crypto.SigningMethod) error + + // Serialize serializes the JWT into its on-the-wire + // representation. + Serialize(key interface{}) ([]byte, error) +}