
37 changed files with 906 additions and 1147 deletions
@ -0,0 +1,202 @@
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "{}" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright {yyyy} {name of copyright owner} |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
|
@ -0,0 +1,5 @@
|
||||
CoreOS Project |
||||
Copyright 2014 CoreOS, Inc |
||||
|
||||
This product includes software developed at CoreOS, Inc. |
||||
(http://www.coreos.com/). |
@ -0,0 +1,126 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"math" |
||||
"time" |
||||
) |
||||
|
||||
type Claims map[string]interface{} |
||||
|
||||
func (c Claims) Add(name string, value interface{}) { |
||||
c[name] = value |
||||
} |
||||
|
||||
func (c Claims) StringClaim(name string) (string, bool, error) { |
||||
cl, ok := c[name] |
||||
if !ok { |
||||
return "", false, nil |
||||
} |
||||
|
||||
v, ok := cl.(string) |
||||
if !ok { |
||||
return "", false, fmt.Errorf("unable to parse claim as string: %v", name) |
||||
} |
||||
|
||||
return v, true, nil |
||||
} |
||||
|
||||
func (c Claims) StringsClaim(name string) ([]string, bool, error) { |
||||
cl, ok := c[name] |
||||
if !ok { |
||||
return nil, false, nil |
||||
} |
||||
|
||||
if v, ok := cl.([]string); ok { |
||||
return v, true, nil |
||||
} |
||||
|
||||
// When unmarshaled, []string will become []interface{}.
|
||||
if v, ok := cl.([]interface{}); ok { |
||||
var ret []string |
||||
for _, vv := range v { |
||||
str, ok := vv.(string) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) |
||||
} |
||||
ret = append(ret, str) |
||||
} |
||||
return ret, true, nil |
||||
} |
||||
|
||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) |
||||
} |
||||
|
||||
func (c Claims) Int64Claim(name string) (int64, bool, error) { |
||||
cl, ok := c[name] |
||||
if !ok { |
||||
return 0, false, nil |
||||
} |
||||
|
||||
v, ok := cl.(int64) |
||||
if !ok { |
||||
vf, ok := cl.(float64) |
||||
if !ok { |
||||
return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name) |
||||
} |
||||
v = int64(vf) |
||||
} |
||||
|
||||
return v, true, nil |
||||
} |
||||
|
||||
func (c Claims) Float64Claim(name string) (float64, bool, error) { |
||||
cl, ok := c[name] |
||||
if !ok { |
||||
return 0, false, nil |
||||
} |
||||
|
||||
v, ok := cl.(float64) |
||||
if !ok { |
||||
vi, ok := cl.(int64) |
||||
if !ok { |
||||
return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name) |
||||
} |
||||
v = float64(vi) |
||||
} |
||||
|
||||
return v, true, nil |
||||
} |
||||
|
||||
func (c Claims) TimeClaim(name string) (time.Time, bool, error) { |
||||
v, ok, err := c.Float64Claim(name) |
||||
if !ok || err != nil { |
||||
return time.Time{}, ok, err |
||||
} |
||||
|
||||
s := math.Trunc(v) |
||||
ns := (v - s) * math.Pow(10, 9) |
||||
return time.Unix(int64(s), int64(ns)).UTC(), true, nil |
||||
} |
||||
|
||||
func decodeClaims(payload []byte) (Claims, error) { |
||||
var c Claims |
||||
if err := json.Unmarshal(payload, &c); err != nil { |
||||
return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err) |
||||
} |
||||
return c, nil |
||||
} |
||||
|
||||
func marshalClaims(c Claims) ([]byte, error) { |
||||
b, err := json.Marshal(c) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return b, nil |
||||
} |
||||
|
||||
func encodeClaims(c Claims) (string, error) { |
||||
b, err := marshalClaims(c) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return encodeSegment(b), nil |
||||
} |
@ -0,0 +1,2 @@
|
||||
// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead.
|
||||
package jose |
@ -0,0 +1,112 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"encoding/json" |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
const ( |
||||
HeaderMediaType = "typ" |
||||
HeaderKeyAlgorithm = "alg" |
||||
HeaderKeyID = "kid" |
||||
) |
||||
|
||||
const ( |
||||
// Encryption Algorithm Header Parameter Values for JWS
|
||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6
|
||||
AlgHS256 = "HS256" |
||||
AlgHS384 = "HS384" |
||||
AlgHS512 = "HS512" |
||||
AlgRS256 = "RS256" |
||||
AlgRS384 = "RS384" |
||||
AlgRS512 = "RS512" |
||||
AlgES256 = "ES256" |
||||
AlgES384 = "ES384" |
||||
AlgES512 = "ES512" |
||||
AlgPS256 = "PS256" |
||||
AlgPS384 = "PS384" |
||||
AlgPS512 = "PS512" |
||||
AlgNone = "none" |
||||
) |
||||
|
||||
const ( |
||||
// Algorithm Header Parameter Values for JWE
|
||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1
|
||||
AlgRSA15 = "RSA1_5" |
||||
AlgRSAOAEP = "RSA-OAEP" |
||||
AlgRSAOAEP256 = "RSA-OAEP-256" |
||||
AlgA128KW = "A128KW" |
||||
AlgA192KW = "A192KW" |
||||
AlgA256KW = "A256KW" |
||||
AlgDir = "dir" |
||||
AlgECDHES = "ECDH-ES" |
||||
AlgECDHESA128KW = "ECDH-ES+A128KW" |
||||
AlgECDHESA192KW = "ECDH-ES+A192KW" |
||||
AlgECDHESA256KW = "ECDH-ES+A256KW" |
||||
AlgA128GCMKW = "A128GCMKW" |
||||
AlgA192GCMKW = "A192GCMKW" |
||||
AlgA256GCMKW = "A256GCMKW" |
||||
AlgPBES2HS256A128KW = "PBES2-HS256+A128KW" |
||||
AlgPBES2HS384A192KW = "PBES2-HS384+A192KW" |
||||
AlgPBES2HS512A256KW = "PBES2-HS512+A256KW" |
||||
) |
||||
|
||||
const ( |
||||
// Encryption Algorithm Header Parameter Values for JWE
|
||||
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22
|
||||
EncA128CBCHS256 = "A128CBC-HS256" |
||||
EncA128CBCHS384 = "A128CBC-HS384" |
||||
EncA256CBCHS512 = "A256CBC-HS512" |
||||
EncA128GCM = "A128GCM" |
||||
EncA192GCM = "A192GCM" |
||||
EncA256GCM = "A256GCM" |
||||
) |
||||
|
||||
type JOSEHeader map[string]string |
||||
|
||||
func (j JOSEHeader) Validate() error { |
||||
if _, exists := j[HeaderKeyAlgorithm]; !exists { |
||||
return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func decodeHeader(seg string) (JOSEHeader, error) { |
||||
b, err := decodeSegment(seg) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var h JOSEHeader |
||||
err = json.Unmarshal(b, &h) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return h, nil |
||||
} |
||||
|
||||
func encodeHeader(h JOSEHeader) (string, error) { |
||||
b, err := json.Marshal(h) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return encodeSegment(b), nil |
||||
} |
||||
|
||||
// Decode JWT specific base64url encoding with padding stripped
|
||||
func decodeSegment(seg string) ([]byte, error) { |
||||
if l := len(seg) % 4; l != 0 { |
||||
seg += strings.Repeat("=", 4-l) |
||||
} |
||||
return base64.URLEncoding.DecodeString(seg) |
||||
} |
||||
|
||||
// Encode JWT specific base64url encoding with padding stripped
|
||||
func encodeSegment(seg []byte) string { |
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") |
||||
} |
@ -0,0 +1,135 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/base64" |
||||
"encoding/binary" |
||||
"encoding/json" |
||||
"math/big" |
||||
"strings" |
||||
) |
||||
|
||||
// JSON Web Key
|
||||
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5
|
||||
type JWK struct { |
||||
ID string |
||||
Type string |
||||
Alg string |
||||
Use string |
||||
Exponent int |
||||
Modulus *big.Int |
||||
Secret []byte |
||||
} |
||||
|
||||
type jwkJSON struct { |
||||
ID string `json:"kid"` |
||||
Type string `json:"kty"` |
||||
Alg string `json:"alg"` |
||||
Use string `json:"use"` |
||||
Exponent string `json:"e"` |
||||
Modulus string `json:"n"` |
||||
} |
||||
|
||||
func (j *JWK) MarshalJSON() ([]byte, error) { |
||||
t := jwkJSON{ |
||||
ID: j.ID, |
||||
Type: j.Type, |
||||
Alg: j.Alg, |
||||
Use: j.Use, |
||||
Exponent: encodeExponent(j.Exponent), |
||||
Modulus: encodeModulus(j.Modulus), |
||||
} |
||||
|
||||
return json.Marshal(&t) |
||||
} |
||||
|
||||
func (j *JWK) UnmarshalJSON(data []byte) error { |
||||
var t jwkJSON |
||||
err := json.Unmarshal(data, &t) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
e, err := decodeExponent(t.Exponent) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
n, err := decodeModulus(t.Modulus) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
j.ID = t.ID |
||||
j.Type = t.Type |
||||
j.Alg = t.Alg |
||||
j.Use = t.Use |
||||
j.Exponent = e |
||||
j.Modulus = n |
||||
|
||||
return nil |
||||
} |
||||
|
||||
type JWKSet struct { |
||||
Keys []JWK `json:"keys"` |
||||
} |
||||
|
||||
func decodeExponent(e string) (int, error) { |
||||
decE, err := decodeBase64URLPaddingOptional(e) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
var eBytes []byte |
||||
if len(decE) < 8 { |
||||
eBytes = make([]byte, 8-len(decE), 8) |
||||
eBytes = append(eBytes, decE...) |
||||
} else { |
||||
eBytes = decE |
||||
} |
||||
eReader := bytes.NewReader(eBytes) |
||||
var E uint64 |
||||
err = binary.Read(eReader, binary.BigEndian, &E) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return int(E), nil |
||||
} |
||||
|
||||
func encodeExponent(e int) string { |
||||
b := make([]byte, 8) |
||||
binary.BigEndian.PutUint64(b, uint64(e)) |
||||
var idx int |
||||
for ; idx < 8; idx++ { |
||||
if b[idx] != 0x0 { |
||||
break |
||||
} |
||||
} |
||||
return base64.RawURLEncoding.EncodeToString(b[idx:]) |
||||
} |
||||
|
||||
// Turns a URL encoded modulus of a key into a big int.
|
||||
func decodeModulus(n string) (*big.Int, error) { |
||||
decN, err := decodeBase64URLPaddingOptional(n) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
N := big.NewInt(0) |
||||
N.SetBytes(decN) |
||||
return N, nil |
||||
} |
||||
|
||||
func encodeModulus(n *big.Int) string { |
||||
return base64.RawURLEncoding.EncodeToString(n.Bytes()) |
||||
} |
||||
|
||||
// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not.
|
||||
// The stdlib version currently doesn't handle this.
|
||||
// We can get rid of this is if this bug:
|
||||
// https://github.com/golang/go/issues/4237
|
||||
// ever closes.
|
||||
func decodeBase64URLPaddingOptional(e string) ([]byte, error) { |
||||
if m := len(e) % 4; m != 0 { |
||||
e += strings.Repeat("=", 4-m) |
||||
} |
||||
return base64.URLEncoding.DecodeString(e) |
||||
} |
@ -0,0 +1,51 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
type JWS struct { |
||||
RawHeader string |
||||
Header JOSEHeader |
||||
RawPayload string |
||||
Payload []byte |
||||
Signature []byte |
||||
} |
||||
|
||||
// Given a raw encoded JWS token parses it and verifies the structure.
|
||||
func ParseJWS(raw string) (JWS, error) { |
||||
parts := strings.Split(raw, ".") |
||||
if len(parts) != 3 { |
||||
return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts)) |
||||
} |
||||
|
||||
rawSig := parts[2] |
||||
jws := JWS{ |
||||
RawHeader: parts[0], |
||||
RawPayload: parts[1], |
||||
} |
||||
|
||||
header, err := decodeHeader(jws.RawHeader) |
||||
if err != nil { |
||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err) |
||||
} |
||||
if err = header.Validate(); err != nil { |
||||
return JWS{}, fmt.Errorf("malformed JWS, %s", err) |
||||
} |
||||
jws.Header = header |
||||
|
||||
payload, err := decodeSegment(jws.RawPayload) |
||||
if err != nil { |
||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err) |
||||
} |
||||
jws.Payload = payload |
||||
|
||||
sig, err := decodeSegment(rawSig) |
||||
if err != nil { |
||||
return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err) |
||||
} |
||||
jws.Signature = sig |
||||
|
||||
return jws, nil |
||||
} |
@ -0,0 +1,82 @@
|
||||
package jose |
||||
|
||||
import "strings" |
||||
|
||||
type JWT JWS |
||||
|
||||
func ParseJWT(token string) (jwt JWT, err error) { |
||||
jws, err := ParseJWS(token) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
return JWT(jws), nil |
||||
} |
||||
|
||||
func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) { |
||||
jwt = JWT{} |
||||
|
||||
jwt.Header = header |
||||
jwt.Header[HeaderMediaType] = "JWT" |
||||
|
||||
claimBytes, err := marshalClaims(claims) |
||||
if err != nil { |
||||
return |
||||
} |
||||
jwt.Payload = claimBytes |
||||
|
||||
eh, err := encodeHeader(header) |
||||
if err != nil { |
||||
return |
||||
} |
||||
jwt.RawHeader = eh |
||||
|
||||
ec, err := encodeClaims(claims) |
||||
if err != nil { |
||||
return |
||||
} |
||||
jwt.RawPayload = ec |
||||
|
||||
return |
||||
} |
||||
|
||||
func (j *JWT) KeyID() (string, bool) { |
||||
kID, ok := j.Header[HeaderKeyID] |
||||
return kID, ok |
||||
} |
||||
|
||||
func (j *JWT) Claims() (Claims, error) { |
||||
return decodeClaims(j.Payload) |
||||
} |
||||
|
||||
// Encoded data part of the token which may be signed.
|
||||
func (j *JWT) Data() string { |
||||
return strings.Join([]string{j.RawHeader, j.RawPayload}, ".") |
||||
} |
||||
|
||||
// Full encoded JWT token string in format: header.claims.signature
|
||||
func (j *JWT) Encode() string { |
||||
d := j.Data() |
||||
s := encodeSegment(j.Signature) |
||||
return strings.Join([]string{d, s}, ".") |
||||
} |
||||
|
||||
func NewSignedJWT(claims Claims, s Signer) (*JWT, error) { |
||||
header := JOSEHeader{ |
||||
HeaderKeyAlgorithm: s.Alg(), |
||||
HeaderKeyID: s.ID(), |
||||
} |
||||
|
||||
jwt, err := NewJWT(header, claims) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
sig, err := s.Sign([]byte(jwt.Data())) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
jwt.Signature = sig |
||||
|
||||
return &jwt, nil |
||||
} |
@ -0,0 +1,24 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
type Verifier interface { |
||||
ID() string |
||||
Alg() string |
||||
Verify(sig []byte, data []byte) error |
||||
} |
||||
|
||||
type Signer interface { |
||||
Verifier |
||||
Sign(data []byte) (sig []byte, err error) |
||||
} |
||||
|
||||
func NewVerifier(jwk JWK) (Verifier, error) { |
||||
if jwk.Type != "RSA" { |
||||
return nil, fmt.Errorf("unsupported key type %q", jwk.Type) |
||||
} |
||||
|
||||
return NewVerifierRSA(jwk) |
||||
} |
@ -0,0 +1,67 @@
|
||||
package jose |
||||
|
||||
import ( |
||||
"crypto" |
||||
"crypto/rand" |
||||
"crypto/rsa" |
||||
"fmt" |
||||
) |
||||
|
||||
type VerifierRSA struct { |
||||
KeyID string |
||||
Hash crypto.Hash |
||||
PublicKey rsa.PublicKey |
||||
} |
||||
|
||||
type SignerRSA struct { |
||||
PrivateKey rsa.PrivateKey |
||||
VerifierRSA |
||||
} |
||||
|
||||
func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) { |
||||
if jwk.Alg != "" && jwk.Alg != "RS256" { |
||||
return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg) |
||||
} |
||||
|
||||
v := VerifierRSA{ |
||||
KeyID: jwk.ID, |
||||
PublicKey: rsa.PublicKey{ |
||||
N: jwk.Modulus, |
||||
E: jwk.Exponent, |
||||
}, |
||||
Hash: crypto.SHA256, |
||||
} |
||||
|
||||
return &v, nil |
||||
} |
||||
|
||||
func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA { |
||||
return &SignerRSA{ |
||||
PrivateKey: key, |
||||
VerifierRSA: VerifierRSA{ |
||||
KeyID: kid, |
||||
PublicKey: key.PublicKey, |
||||
Hash: crypto.SHA256, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (v *VerifierRSA) ID() string { |
||||
return v.KeyID |
||||
} |
||||
|
||||
func (v *VerifierRSA) Alg() string { |
||||
return "RS256" |
||||
} |
||||
|
||||
func (v *VerifierRSA) Verify(sig []byte, data []byte) error { |
||||
h := v.Hash.New() |
||||
h.Write(data) |
||||
return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig) |
||||
} |
||||
|
||||
func (s *SignerRSA) Sign(data []byte) ([]byte, error) { |
||||
h := s.Hash.New() |
||||
h.Write(data) |
||||
return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil)) |
||||
} |