| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- //go:generate ./gen.sh
- // Package jws implements the digital signature on JSON based data
- // structures as described in https://tools.ietf.org/html/rfc7515
- //
- // If you do not care about the details, the only things that you
- // would need to use are the following functions:
- //
- // jws.Sign(payload, algorithm, key)
- // jws.Verify(encodedjws, algorithm, key)
- //
- // To sign, simply use `jws.Sign`. `payload` is a []byte buffer that
- // contains whatever data you want to sign. `alg` is one of the
- // jwa.SignatureAlgorithm constants from package jwa. For RSA and
- // ECDSA family of algorithms, you will need to prepare a private key.
- // For HMAC family, you just need a []byte value. The `jws.Sign`
- // function will return the encoded JWS message on success.
- //
- // To verify, use `jws.Verify`. It will parse the `encodedjws` buffer
- // and verify the result using `algorithm` and `key`. Upon successful
- // verification, the original payload is returned, so you can work on it.
- package jws
- import (
- "bufio"
- "bytes"
- "context"
- "crypto/ecdsa"
- "crypto/ed25519"
- "crypto/rsa"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "reflect"
- "strings"
- "sync"
- "unicode"
- "unicode/utf8"
- "github.com/lestrrat-go/backoff/v2"
- "github.com/lestrrat-go/jwx/internal/base64"
- "github.com/lestrrat-go/jwx/internal/json"
- "github.com/lestrrat-go/jwx/internal/pool"
- "github.com/lestrrat-go/jwx/jwa"
- "github.com/lestrrat-go/jwx/jwk"
- "github.com/lestrrat-go/jwx/x25519"
- "github.com/pkg/errors"
- )
- var registry = json.NewRegistry()
- type payloadSigner struct {
- signer Signer
- key interface{}
- protected Headers
- public Headers
- }
- func (s *payloadSigner) Sign(payload []byte) ([]byte, error) {
- return s.signer.Sign(payload, s.key)
- }
- func (s *payloadSigner) Algorithm() jwa.SignatureAlgorithm {
- return s.signer.Algorithm()
- }
- func (s *payloadSigner) ProtectedHeader() Headers {
- return s.protected
- }
- func (s *payloadSigner) PublicHeader() Headers {
- return s.public
- }
- var signers = make(map[jwa.SignatureAlgorithm]Signer)
- var muSigner = &sync.Mutex{}
- // Sign generates a signature for the given payload, and serializes
- // it in compact serialization format. In this format you may NOT use
- // multiple signers.
- //
- // The `alg` parameter is the identifier for the signature algorithm
- // that should be used.
- //
- // For the `key` parameter, any of the following is accepted:
- // * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc)
- // * A crypto.Signer
- // * A jwk.Key
- //
- // A `crypto.Signer` is used when the private part of a key is
- // kept in an inaccessible location, such as hardware.
- // `crypto.Signer` is currently supported for RSA, ECDSA, and EdDSA
- // family of algorithms.
- //
- // If the key is a jwk.Key and the key contains a key ID (`kid` field),
- // then it is added to the protected header generated by the signature
- //
- // The algorithm specified in the `alg` parameter must be able to support
- // the type of key you provided, otherwise an error is returned.
- //
- // If you would like to pass custom headers, use the WithHeaders option.
- //
- // If the headers contain "b64" field, then the boolean value for the field
- // is respected when creating the compact serialization form. That is,
- // if you specify a header with `{"b64": false}`, then the payload is
- // not base64 encoded.
- //
- // If you want to use a detached payload, use `jws.WithDetachedPayload()` as
- // one of the options. When you use this option, you must always set the
- // first parameter (`payload`) to `nil`, or the function will return an error
- func Sign(payload []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) ([]byte, error) {
- var hdrs Headers
- var detached bool
- for _, o := range options {
- //nolint:forcetypeassert
- switch o.Ident() {
- case identHeaders{}:
- hdrs = o.Value().(Headers)
- case identDetachedPayload{}:
- detached = true
- if payload != nil {
- return nil, errors.New(`jws.Sign: payload must be nil when jws.WithDetachedPayload() is specified`)
- }
- payload = o.Value().([]byte)
- }
- }
- muSigner.Lock()
- signer, ok := signers[alg]
- if !ok {
- v, err := NewSigner(alg)
- if err != nil {
- muSigner.Unlock()
- return nil, errors.Wrap(err, `failed to create signer`)
- }
- signers[alg] = v
- signer = v
- }
- muSigner.Unlock()
- // XXX This is cheating. Ideally `detached` should be passed as a parameter
- // but since this is an exported method, we can't change this without bumping
- // major versions.... But we don't want to do that now, so we will cheat by
- // making it part of the object
- sig := &Signature{
- protected: hdrs,
- detached: detached,
- }
- _, signature, err := sig.Sign(payload, signer, key)
- if err != nil {
- return nil, errors.Wrap(err, `failed sign payload`)
- }
- return signature, nil
- }
- // SignMulti accepts multiple signers via the options parameter,
- // and creates a JWS in JSON serialization format that contains
- // signatures from applying aforementioned signers.
- //
- // Use `jws.WithSigner(...)` to specify values how to generate
- // each signature in the `"signatures": [ ... ]` field.
- func SignMulti(payload []byte, options ...Option) ([]byte, error) {
- var signers []*payloadSigner
- for _, o := range options {
- //nolint:forcetypeassert
- switch o.Ident() {
- case identPayloadSigner{}:
- signers = append(signers, o.Value().(*payloadSigner))
- }
- }
- if len(signers) == 0 {
- return nil, errors.New(`no signers provided`)
- }
- var result Message
- result.payload = payload
- result.signatures = make([]*Signature, 0, len(signers))
- for i, signer := range signers {
- protected := signer.ProtectedHeader()
- if protected == nil {
- protected = NewHeaders()
- }
- if err := protected.Set(AlgorithmKey, signer.Algorithm()); err != nil {
- return nil, errors.Wrap(err, `failed to set "alg" header`)
- }
- if key, ok := signer.key.(jwk.Key); ok {
- if kid := key.KeyID(); kid != "" {
- if err := protected.Set(KeyIDKey, kid); err != nil {
- return nil, errors.Wrap(err, `failed to set "kid" header`)
- }
- }
- }
- sig := &Signature{
- headers: signer.PublicHeader(),
- protected: protected,
- }
- _, _, err := sig.Sign(payload, signer.signer, signer.key)
- if err != nil {
- return nil, errors.Wrapf(err, `failed to generate signature for signer #%d (alg=%s)`, i, signer.Algorithm())
- }
- result.signatures = append(result.signatures, sig)
- }
- return json.Marshal(result)
- }
- type verifyCtx struct {
- dst *Message
- detachedPayload []byte
- alg jwa.SignatureAlgorithm
- key interface{}
- useJKU bool
- jwksFetcher JWKSetFetcher
- // This is only used to differentiate compact/JSON serialization
- // because certain features are enabled/disabled in each
- isJSON bool
- }
- var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool {
- return false
- })
- // VerifyAuto is a special case of Verify(), where verification is done
- // using verifications parameters that can be obtained using the information
- // that is carried within the JWS message itself.
- //
- // Currently it only supports verification via `jku` which will be fetched
- // using the object specified in `jws.JWKSetFetcher`. Note that URLs in `jku` can
- // only have https scheme.
- //
- // Using this function will result in your program accessing remote resources via https,
- // and therefore extreme caution should be taken which urls can be accessed.
- //
- // Without specifying extra arguments, the default `jws.JWKSetFetcher` will be
- // configured with a whitelist that rejects *ALL URLSs*. This is to
- // protect users from unintentionally allowing their projects to
- // make unwanted requests. Therefore you must explicitly provide an
- // instance of `jwk.Whitelist` that does what you want.
- //
- // If you want open access to any URLs in the `jku`, you can do this by
- // using `jwk.InsecureWhitelist` as the whitelist, but this should be avoided in
- // most cases, especially if the payload comes from outside of a controlled
- // environment.
- //
- // It is also advised that you consider using some sort of backoff via `jws.WithFetchBackoff`
- //
- // Alternatively, you can provide your own `jws.JWKSetFetcher`. In this case
- // there is no way for the framework to force you to set a whitelist, so the
- // default behavior is to allow any URLs. You are responsible for providing
- // your own safety measures.
- func VerifyAuto(buf []byte, options ...VerifyOption) ([]byte, error) {
- var ctx verifyCtx
- // enable JKU processing
- ctx.useJKU = true
- var fetchOptions []jwk.FetchOption
- //nolint:forcetypeassert
- for _, option := range options {
- switch option.Ident() {
- case identMessage{}:
- ctx.dst = option.Value().(*Message)
- case identDetachedPayload{}:
- ctx.detachedPayload = option.Value().([]byte)
- case identJWKSetFetcher{}:
- ctx.jwksFetcher = option.Value().(JWKSetFetcher)
- case identFetchWhitelist{}:
- fetchOptions = append(fetchOptions, jwk.WithFetchWhitelist(option.Value().(jwk.Whitelist)))
- case identFetchBackoff{}:
- fetchOptions = append(fetchOptions, jwk.WithFetchBackoff(option.Value().(backoff.Policy)))
- case identHTTPClient{}:
- fetchOptions = append(fetchOptions, jwk.WithHTTPClient(option.Value().(*http.Client)))
- }
- }
- // We shove the default Whitelist in the front of the option list.
- // If the user provided one, it will overwrite our default value
- if ctx.jwksFetcher == nil {
- fetchOptions = append([]jwk.FetchOption{jwk.WithFetchWhitelist(allowNoneWhitelist)}, fetchOptions...)
- ctx.jwksFetcher = NewJWKSetFetcher(fetchOptions...)
- }
- return ctx.verify(buf)
- }
- // Verify checks if the given JWS message is verifiable using `alg` and `key`.
- // `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key
- //
- // If the verification is successful, `err` is nil, and the content of the
- // payload that was signed is returned. If you need more fine-grained
- // control of the verification process, manually generate a
- // `Verifier` in `verify` subpackage, and call `Verify` method on it.
- // If you need to access signatures and JOSE headers in a JWS message,
- // use `Parse` function to get `Message` object.
- func Verify(buf []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...VerifyOption) ([]byte, error) {
- var ctx verifyCtx
- ctx.alg = alg
- ctx.key = key
- //nolint:forcetypeassert
- for _, option := range options {
- switch option.Ident() {
- case identMessage{}:
- ctx.dst = option.Value().(*Message)
- case identDetachedPayload{}:
- ctx.detachedPayload = option.Value().([]byte)
- default:
- return nil, errors.Errorf(`invalid jws.VerifyOption %q passed`, `With`+strings.TrimPrefix(fmt.Sprintf(`%T`, option.Ident()), `jws.ident`))
- }
- }
- return ctx.verify(buf)
- }
- func (ctx *verifyCtx) verify(buf []byte) ([]byte, error) {
- buf = bytes.TrimSpace(buf)
- if len(buf) == 0 {
- return nil, errors.New(`attempt to verify empty buffer`)
- }
- if buf[0] == '{' {
- return ctx.verifyJSON(buf)
- }
- return ctx.verifyCompact(buf)
- }
- // VerifySet uses keys store in a jwk.Set to verify the payload in `buf`.
- //
- // In order for `VerifySet()` to use a key in the given set, the
- // `jwk.Key` object must have a valid "alg" field, and it also must
- // have either an empty value or the value "sig" in the "use" field.
- //
- // Furthermore if the JWS signature asks for a spefici "kid", the
- // `jwk.Key` must have the same "kid" as the signature.
- func VerifySet(buf []byte, set jwk.Set) ([]byte, error) {
- n := set.Len()
- for i := 0; i < n; i++ {
- key, ok := set.Get(i)
- if !ok {
- continue
- }
- if key.Algorithm() == "" { // algorithm is not
- continue
- }
- if usage := key.KeyUsage(); usage != "" && usage != jwk.ForSignature.String() {
- continue
- }
- buf, err := Verify(buf, jwa.SignatureAlgorithm(key.Algorithm()), key)
- if err != nil {
- continue
- }
- return buf, nil
- }
- return nil, errors.New(`failed to verify message with any of the keys in the jwk.Set object`)
- }
- func (ctx *verifyCtx) verifyJSON(signed []byte) ([]byte, error) {
- ctx.isJSON = true
- var m Message
- m.SetDecodeCtx(collectRawCtx{})
- defer m.clearRaw()
- if err := json.Unmarshal(signed, &m); err != nil {
- return nil, errors.Wrap(err, `failed to unmarshal JSON message`)
- }
- m.SetDecodeCtx(nil)
- if len(m.payload) != 0 && ctx.detachedPayload != nil {
- return nil, errors.New(`can't specify detached payload for JWS with payload`)
- }
- if ctx.detachedPayload != nil {
- m.payload = ctx.detachedPayload
- }
- // Pre-compute the base64 encoded version of payload
- var payload string
- if m.b64 {
- payload = base64.EncodeToString(m.payload)
- } else {
- payload = string(m.payload)
- }
- buf := pool.GetBytesBuffer()
- defer pool.ReleaseBytesBuffer(buf)
- for i, sig := range m.signatures {
- buf.Reset()
- var encodedProtectedHeader string
- if rbp, ok := sig.protected.(interface{ rawBuffer() []byte }); ok {
- if raw := rbp.rawBuffer(); raw != nil {
- encodedProtectedHeader = base64.EncodeToString(raw)
- }
- }
- if encodedProtectedHeader == "" {
- protected, err := json.Marshal(sig.protected)
- if err != nil {
- return nil, errors.Wrapf(err, `failed to marshal "protected" for signature #%d`, i+1)
- }
- encodedProtectedHeader = base64.EncodeToString(protected)
- }
- buf.WriteString(encodedProtectedHeader)
- buf.WriteByte('.')
- buf.WriteString(payload)
- if !ctx.useJKU {
- if hdr := sig.protected; hdr != nil && hdr.KeyID() != "" {
- if jwkKey, ok := ctx.key.(jwk.Key); ok {
- if jwkKey.KeyID() != hdr.KeyID() {
- continue
- }
- }
- }
- verifier, err := NewVerifier(ctx.alg)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create verifier")
- }
- if _, err := ctx.tryVerify(verifier, sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil {
- if ctx.dst != nil {
- *(ctx.dst) = m
- }
- return m.payload, nil
- }
- // Don't fallthrough or bail out. Try the next signature.
- continue
- }
- if _, err := ctx.verifyJKU(sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil {
- if ctx.dst != nil {
- *(ctx.dst) = m
- }
- return m.payload, nil
- }
- // try next
- }
- return nil, errors.New(`could not verify with any of the signatures`)
- }
- // get the value of b64 header field.
- // If the field does not exist, returns true (default)
- // Otherwise return the value specified by the header field.
- func getB64Value(hdr Headers) bool {
- b64raw, ok := hdr.Get("b64")
- if !ok {
- return true // default
- }
- b64, ok := b64raw.(bool) // default
- if !ok {
- return false
- }
- return b64
- }
- func (ctx *verifyCtx) verifyCompact(signed []byte) ([]byte, error) {
- protected, payload, signature, err := SplitCompact(signed)
- if err != nil {
- return nil, errors.Wrap(err, `failed extract from compact serialization format`)
- }
- decodedSignature, err := base64.Decode(signature)
- if err != nil {
- return nil, errors.Wrap(err, `failed to decode signature`)
- }
- hdr := NewHeaders()
- decodedProtected, err := base64.Decode(protected)
- if err != nil {
- return nil, errors.Wrap(err, `failed to decode headers`)
- }
- if err := json.Unmarshal(decodedProtected, hdr); err != nil {
- return nil, errors.Wrap(err, `failed to decode headers`)
- }
- verifyBuf := pool.GetBytesBuffer()
- defer pool.ReleaseBytesBuffer(verifyBuf)
- verifyBuf.Write(protected)
- verifyBuf.WriteByte('.')
- if len(payload) == 0 && ctx.detachedPayload != nil {
- if getB64Value(hdr) {
- payload = base64.Encode(ctx.detachedPayload)
- } else {
- payload = ctx.detachedPayload
- }
- }
- verifyBuf.Write(payload)
- if !ctx.useJKU {
- if hdr.KeyID() != "" {
- if jwkKey, ok := ctx.key.(jwk.Key); ok {
- if jwkKey.KeyID() != hdr.KeyID() {
- return nil, errors.New(`"kid" fields do not match`)
- }
- }
- }
- verifier, err := NewVerifier(ctx.alg)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create verifier")
- }
- return ctx.tryVerify(verifier, hdr, verifyBuf.Bytes(), decodedSignature, payload)
- }
- return ctx.verifyJKU(hdr, verifyBuf.Bytes(), decodedSignature, payload)
- }
- // JWKSetFetcher is used to fetch JWK Set spcified in the `jku` field.
- type JWKSetFetcher interface {
- Fetch(string) (jwk.Set, error)
- }
- // SimpleJWKSetFetcher is the default object used to fetch JWK Sets specified in `jku`,
- // which uses `jwk.Fetch()`
- //
- // For more complicated cases, such as using `jwk.AutoRefetch`, you will have to
- // create your custom instance of `jws.JWKSetFetcher`
- type SimpleJWKSetFetcher struct {
- options []jwk.FetchOption
- }
- func NewJWKSetFetcher(options ...jwk.FetchOption) *SimpleJWKSetFetcher {
- return &SimpleJWKSetFetcher{options: options}
- }
- func (f *SimpleJWKSetFetcher) Fetch(u string) (jwk.Set, error) {
- return jwk.Fetch(context.TODO(), u, f.options...)
- }
- type JWKSetFetchFunc func(string) (jwk.Set, error)
- func (f JWKSetFetchFunc) Fetch(u string) (jwk.Set, error) {
- return f(u)
- }
- func (ctx *verifyCtx) verifyJKU(hdr Headers, verifyBuf, decodedSignature, payload []byte) ([]byte, error) {
- u := hdr.JWKSetURL()
- if u == "" {
- return nil, errors.New(`use of "jku" field specified, but the field is empty`)
- }
- uo, err := url.Parse(u)
- if err != nil {
- return nil, errors.Wrap(err, `failed to parse "jku"`)
- }
- if uo.Scheme != "https" {
- return nil, errors.New(`url in "jku" must be HTTPS`)
- }
- set, err := ctx.jwksFetcher.Fetch(u)
- if err != nil {
- return nil, errors.Wrapf(err, `failed to fetch "jku"`)
- }
- // Because we're using a JWKS here, we MUST have "kid" that matches
- // the payload
- if hdr.KeyID() == "" {
- return nil, errors.Errorf(`"kid" is required on the JWS message to use "jku"`)
- }
- key, ok := set.LookupKeyID(hdr.KeyID())
- if !ok {
- return nil, errors.New(`key specified via "kid" is not present in the JWK set specified by "jku"`)
- }
- // hooray, we found a key. Now the algorithm will have to be inferred.
- algs, err := AlgorithmsForKey(key)
- if err != nil {
- return nil, errors.Wrapf(err, `failed to get a list of signature methods for key type %s`, key.KeyType())
- }
- // for each of these algorithms, just ... keep trying ...
- ctx.key = key
- hdrAlg := hdr.Algorithm()
- for _, alg := range algs {
- // if we have a "alg" field in the JWS, we can only proceed if
- // the inferred algorithm matches
- if hdrAlg != "" && hdrAlg != alg {
- continue
- }
- verifier, err := NewVerifier(alg)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create verifier")
- }
- if decoded, err := ctx.tryVerify(verifier, hdr, verifyBuf, decodedSignature, payload); err == nil {
- return decoded, nil
- }
- }
- return nil, errors.New(`failed to verify payload using key in "jku"`)
- }
- func (ctx *verifyCtx) tryVerify(verifier Verifier, hdr Headers, buf, decodedSignature, payload []byte) ([]byte, error) {
- if err := verifier.Verify(buf, decodedSignature, ctx.key); err != nil {
- return nil, errors.Wrap(err, `failed to verify message`)
- }
- var decodedPayload []byte
- // When verifying JSON messages, we do not need to decode
- // the payload, as we already have it
- if !ctx.isJSON {
- // This is a special case for RFC7797
- if !getB64Value(hdr) { // it's not base64 encoded
- decodedPayload = payload
- }
- if decodedPayload == nil {
- v, err := base64.Decode(payload)
- if err != nil {
- return nil, errors.Wrap(err, `message verified, failed to decode payload`)
- }
- decodedPayload = v
- }
- // For compact serialization, we need to create and assign the message
- // if requested
- if ctx.dst != nil {
- // Construct a new Message object
- m := NewMessage()
- m.SetPayload(decodedPayload)
- sig := NewSignature()
- sig.SetProtectedHeaders(hdr)
- sig.SetSignature(decodedSignature)
- m.AppendSignature(sig)
- *(ctx.dst) = *m
- }
- }
- return decodedPayload, nil
- }
- // This is an "optimized" ioutil.ReadAll(). It will attempt to read
- // all of the contents from the reader IF the reader is of a certain
- // concrete type.
- func readAll(rdr io.Reader) ([]byte, bool) {
- switch rdr.(type) {
- case *bytes.Reader, *bytes.Buffer, *strings.Reader:
- data, err := ioutil.ReadAll(rdr)
- if err != nil {
- return nil, false
- }
- return data, true
- default:
- return nil, false
- }
- }
- // Parse parses contents from the given source and creates a jws.Message
- // struct. The input can be in either compact or full JSON serialization.
- func Parse(src []byte) (*Message, error) {
- for i := 0; i < len(src); i++ {
- r := rune(src[i])
- if r >= utf8.RuneSelf {
- r, _ = utf8.DecodeRune(src)
- }
- if !unicode.IsSpace(r) {
- if r == '{' {
- return parseJSON(src)
- }
- return parseCompact(src)
- }
- }
- return nil, errors.New("invalid byte sequence")
- }
- // Parse parses contents from the given source and creates a jws.Message
- // struct. The input can be in either compact or full JSON serialization.
- func ParseString(src string) (*Message, error) {
- return Parse([]byte(src))
- }
- // Parse parses contents from the given source and creates a jws.Message
- // struct. The input can be in either compact or full JSON serialization.
- func ParseReader(src io.Reader) (*Message, error) {
- if data, ok := readAll(src); ok {
- return Parse(data)
- }
- rdr := bufio.NewReader(src)
- var first rune
- for {
- r, _, err := rdr.ReadRune()
- if err != nil {
- return nil, errors.Wrap(err, `failed to read rune`)
- }
- if !unicode.IsSpace(r) {
- first = r
- if err := rdr.UnreadRune(); err != nil {
- return nil, errors.Wrap(err, `failed to unread rune`)
- }
- break
- }
- }
- var parser func(io.Reader) (*Message, error)
- if first == '{' {
- parser = parseJSONReader
- } else {
- parser = parseCompactReader
- }
- m, err := parser(rdr)
- if err != nil {
- return nil, errors.Wrap(err, `failed to parse jws message`)
- }
- return m, nil
- }
- func parseJSONReader(src io.Reader) (result *Message, err error) {
- var m Message
- if err := json.NewDecoder(src).Decode(&m); err != nil {
- return nil, errors.Wrap(err, `failed to unmarshal jws message`)
- }
- return &m, nil
- }
- func parseJSON(data []byte) (result *Message, err error) {
- var m Message
- if err := json.Unmarshal(data, &m); err != nil {
- return nil, errors.Wrap(err, `failed to unmarshal jws message`)
- }
- return &m, nil
- }
- // SplitCompact splits a JWT and returns its three parts
- // separately: protected headers, payload and signature.
- func SplitCompact(src []byte) ([]byte, []byte, []byte, error) {
- parts := bytes.Split(src, []byte("."))
- if len(parts) < 3 {
- return nil, nil, nil, errors.New(`invalid number of segments`)
- }
- return parts[0], parts[1], parts[2], nil
- }
- // SplitCompactString splits a JWT and returns its three parts
- // separately: protected headers, payload and signature.
- func SplitCompactString(src string) ([]byte, []byte, []byte, error) {
- parts := strings.Split(src, ".")
- if len(parts) < 3 {
- return nil, nil, nil, errors.New(`invalid number of segments`)
- }
- return []byte(parts[0]), []byte(parts[1]), []byte(parts[2]), nil
- }
- // SplitCompactReader splits a JWT and returns its three parts
- // separately: protected headers, payload and signature.
- func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) {
- if data, ok := readAll(rdr); ok {
- return SplitCompact(data)
- }
- var protected []byte
- var payload []byte
- var signature []byte
- var periods int
- var state int
- buf := make([]byte, 4096)
- var sofar []byte
- for {
- // read next bytes
- n, err := rdr.Read(buf)
- // return on unexpected read error
- if err != nil && err != io.EOF {
- return nil, nil, nil, errors.Wrap(err, `unexpected end of input`)
- }
- // append to current buffer
- sofar = append(sofar, buf[:n]...)
- // loop to capture multiple '.' in current buffer
- for loop := true; loop; {
- var i = bytes.IndexByte(sofar, '.')
- if i == -1 && err != io.EOF {
- // no '.' found -> exit and read next bytes (outer loop)
- loop = false
- continue
- } else if i == -1 && err == io.EOF {
- // no '.' found -> process rest and exit
- i = len(sofar)
- loop = false
- } else {
- // '.' found
- periods++
- }
- // Reaching this point means we have found a '.' or EOF and process the rest of the buffer
- switch state {
- case 0:
- protected = sofar[:i]
- state++
- case 1:
- payload = sofar[:i]
- state++
- case 2:
- signature = sofar[:i]
- }
- // Shorten current buffer
- if len(sofar) > i {
- sofar = sofar[i+1:]
- }
- }
- // Exit on EOF
- if err == io.EOF {
- break
- }
- }
- if periods != 2 {
- return nil, nil, nil, errors.New(`invalid number of segments`)
- }
- return protected, payload, signature, nil
- }
- // parseCompactReader parses a JWS value serialized via compact serialization.
- func parseCompactReader(rdr io.Reader) (m *Message, err error) {
- protected, payload, signature, err := SplitCompactReader(rdr)
- if err != nil {
- return nil, errors.Wrap(err, `invalid compact serialization format`)
- }
- return parse(protected, payload, signature)
- }
- func parseCompact(data []byte) (m *Message, err error) {
- protected, payload, signature, err := SplitCompact(data)
- if err != nil {
- return nil, errors.Wrap(err, `invalid compact serialization format`)
- }
- return parse(protected, payload, signature)
- }
- func parse(protected, payload, signature []byte) (*Message, error) {
- decodedHeader, err := base64.Decode(protected)
- if err != nil {
- return nil, errors.Wrap(err, `failed to decode protected headers`)
- }
- hdr := NewHeaders()
- if err := json.Unmarshal(decodedHeader, hdr); err != nil {
- return nil, errors.Wrap(err, `failed to parse JOSE headers`)
- }
- decodedPayload, err := base64.Decode(payload)
- if err != nil {
- return nil, errors.Wrap(err, `failed to decode payload`)
- }
- decodedSignature, err := base64.Decode(signature)
- if err != nil {
- return nil, errors.Wrap(err, `failed to decode signature`)
- }
- var msg Message
- msg.payload = decodedPayload
- msg.signatures = append(msg.signatures, &Signature{
- protected: hdr,
- signature: decodedSignature,
- })
- return &msg, nil
- }
- // RegisterCustomField allows users to specify that a private field
- // be decoded as an instance of the specified type. This option has
- // a global effect.
- //
- // For example, suppose you have a custom field `x-birthday`, which
- // you want to represent as a string formatted in RFC3339 in JSON,
- // but want it back as `time.Time`.
- //
- // In that case you would register a custom field as follows
- //
- // jwe.RegisterCustomField(`x-birthday`, timeT)
- //
- // Then `hdr.Get("x-birthday")` will still return an `interface{}`,
- // but you can convert its type to `time.Time`
- //
- // bdayif, _ := hdr.Get(`x-birthday`)
- // bday := bdayif.(time.Time)
- //
- func RegisterCustomField(name string, object interface{}) {
- registry.Register(name, object)
- }
- // Helpers for signature verification
- var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType)
- var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm)
- func init() {
- rawKeyToKeyType[reflect.TypeOf([]byte(nil))] = jwa.OctetSeq
- rawKeyToKeyType[reflect.TypeOf(ed25519.PublicKey(nil))] = jwa.OKP
- rawKeyToKeyType[reflect.TypeOf(rsa.PublicKey{})] = jwa.RSA
- rawKeyToKeyType[reflect.TypeOf((*rsa.PublicKey)(nil))] = jwa.RSA
- rawKeyToKeyType[reflect.TypeOf(ecdsa.PublicKey{})] = jwa.EC
- rawKeyToKeyType[reflect.TypeOf((*ecdsa.PublicKey)(nil))] = jwa.EC
- addAlgorithmForKeyType(jwa.OKP, jwa.EdDSA)
- for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
- addAlgorithmForKeyType(jwa.OctetSeq, alg)
- }
- for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
- addAlgorithmForKeyType(jwa.RSA, alg)
- }
- for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} {
- addAlgorithmForKeyType(jwa.EC, alg)
- }
- }
- func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) {
- keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg)
- }
- // AlgorithmsForKey returns the possible signature algorithms that can
- // be used for a given key. It only takes in consideration keys/algorithms
- // for verification purposes, as this is the only usage where one may need
- // dynamically figure out which method to use.
- func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error) {
- var kty jwa.KeyType
- switch key := key.(type) {
- case jwk.Key:
- kty = key.KeyType()
- case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey:
- kty = jwa.RSA
- case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey:
- kty = jwa.EC
- case ed25519.PublicKey, ed25519.PrivateKey, x25519.PublicKey, x25519.PrivateKey:
- kty = jwa.OKP
- case []byte:
- kty = jwa.OctetSeq
- default:
- return nil, errors.Errorf(`invalid key %T`, key)
- }
- algs, ok := keyTypeToAlgorithms[kty]
- if !ok {
- return nil, errors.Errorf(`invalid key type %q`, kty)
- }
- return algs, nil
- }
|