diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
| commit | 20ef0d92694465ac86b550df139e8366a0a2b4fa (patch) | |
| tree | 3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/dvsekhvalnov | |
| parent | 44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff) | |
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/dvsekhvalnov')
36 files changed, 3409 insertions, 0 deletions
diff --git a/vendor/github.com/dvsekhvalnov/jose2go/.gitignore b/vendor/github.com/dvsekhvalnov/jose2go/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/dvsekhvalnov/jose2go/LICENSE b/vendor/github.com/dvsekhvalnov/jose2go/LICENSE new file mode 100644 index 0000000..326fe25 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/dvsekhvalnov/jose2go/README.md b/vendor/github.com/dvsekhvalnov/jose2go/README.md new file mode 100644 index 0000000..bbf0ef7 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/README.md @@ -0,0 +1,1023 @@ +# Golang (GO) Javascript Object Signing and Encryption (JOSE) and JSON Web Token (JWT) implementation + +[](http://godoc.org/github.com/dvsekhvalnov/jose2go) + +Pure Golang (GO) library for generating, decoding and encrypting [JSON Web Tokens](https://tools.ietf.org/html/rfc7519). Zero dependency, relies only +on standard library. + +Supports full suite of signing, encryption and compression algorithms defined by [JSON Web Algorithms](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-31) as of July 4, 2014 version. + +Extensively unit tested and cross tested (100+ tests) for compatibility with [jose.4.j](https://bitbucket.org/b_c/jose4j/wiki/Home), [Nimbus-JOSE-JWT](https://bitbucket.org/nimbusds/nimbus-jose-jwt/wiki/Home), [json-jwt](https://github.com/nov/json-jwt) and +[jose-jwt](https://github.com/dvsekhvalnov/jose-jwt) libraries. + + +## Status +Used in production. GA ready. Current version is 1.6. + +## Important +v1.6 security tuning options + +v1.5 bug fix release + +v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no +extra headers added by library automatically. To mimic pre 1.4 behaviour use: +```Go +token, err := jose.Sign(..., jose.Header("typ", "JWT")) + +//or + +token, err := jose.Encrypt(..., jose.Header("typ", "JWT")) +``` + +v1.3 fixed potential Invalid Curve Attack on NIST curves within ECDH key management. +Upgrade strongly recommended. + +v1.2 breaks `jose.Decode` interface by returning 3 values instead of 2. + +v1.2 deprecates `jose.Compress` method in favor of using configuration options to `jose.Encrypt`, +the method will be removed in next release. + +### Migration to v1.2 +Pre v1.2 decoding: + +```Go +payload,err := jose.Decode(token,sharedKey) +``` + +Should be updated to v1.2: + +```Go +payload, headers, err := jose.Decode(token,sharedKey) +``` + +Pre v1.2 compression: + +```Go +token,err := jose.Compress(payload,jose.DIR,jose.A128GCM,jose.DEF, key) +``` + +Should be update to v1.2: + +```Go +token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, key, jose.Zip(jose.DEF)) +``` + +## Supported JWA algorithms + +**Signing** +- HMAC signatures with HS256, HS384 and HS512. +- RSASSA-PKCS1-V1_5 signatures with RS256, RS384 and RS512. +- RSASSA-PSS signatures (probabilistic signature scheme with appendix) with PS256, PS384 and PS512. +- ECDSA signatures with ES256, ES384 and ES512. +- NONE (unprotected) plain text algorithm without integrity protection + +**Encryption** +- RSAES OAEP (using SHA-1 and MGF1 with SHA-1) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- RSAES OAEP 256 (using SHA-256 and MGF1 with SHA-256) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- RSAES-PKCS1-V1_5 encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- A128KW, A192KW, A256KW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- A128GCMKW, A192GCMKW, A256GCMKW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- ECDH-ES with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM +- Direct symmetric key encryption with pre-shared key A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM and A256GCM + +**Compression** +- DEFLATE compression + +## Installation +### Grab package from github +`go get github.com/dvsekhvalnov/jose2go` or `go get -u github.com/dvsekhvalnov/jose2go` to update to latest version + +### Import package +```Go +import ( + "github.com/dvsekhvalnov/jose2go" +) +``` + +## Usage +#### Creating Plaintext (unprotected) Tokens + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + token,err := jose.Sign(payload,jose.NONE, nil) + + if(err==nil) { + //go use token + fmt.Printf("\nPlaintext = %v\n",token) + } +} +``` + +### Creating signed tokens +#### HS-256, HS-384 and HS-512 +Signing with HS256, HS384, HS512 expecting `[]byte` array key of corresponding length: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + key := []byte{97,48,97,50,97,98,100,56,45,54,49,54,50,45,52,49,99,51,45,56,51,100,54,45,49,99,102,53,53,57,98,52,54,97,102,99} + + token,err := jose.Sign(payload,jose.HS256,key) + + if(err==nil) { + //go use token + fmt.Printf("\nHS256 = %v\n",token) + } +} +``` + +#### RS-256, RS-384 and RS-512, PS-256, PS-384 and PS-512 +Signing with RS256, RS384, RS512, PS256, PS384, PS512 expecting `*rsa.PrivateKey` private key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*rsa.PrivateKey` instance from PEM encoded PKCS1 or PKCS8 data: `Rsa.ReadPrivate([]byte)` under `jose2go/keys/rsa` package. + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + keyBytes,err := ioutil.ReadFile("private.key") + + if(err!=nil) { + panic("invalid key file") + } + + privateKey,e:=Rsa.ReadPrivate(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + token,err := jose.Sign(payload,jose.RS256, privateKey) + + if(err==nil) { + //go use token + fmt.Printf("\nRS256 = %v\n",token) + } +} +``` + +#### ES-256, ES-384 and ES-512 +ES256, ES384, ES512 ECDSA signatures expecting `*ecdsa.PrivateKey` private elliptic curve key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*ecdsa.PrivateKey` instance from PEM encoded PKCS1 or PKCS8 data: `ecc.ReadPrivate([]byte)` or directly from `X,Y,D` parameters: `ecc.NewPrivate(x,y,d []byte)` under `jose2go/keys/ecc` package. + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello":"world"}` + + privateKey:=ecc.NewPrivate([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}, + []byte{ 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 }) + + token,err := jose.Sign(payload, jose.ES256, privateKey) + + if(err==nil) { + //go use token + fmt.Printf("\ntoken = %v\n",token) + } +} +``` + +### Creating encrypted tokens +#### RSA-OAEP-256, RSA-OAEP and RSA1\_5 key management algorithm +RSA-OAEP-256, RSA-OAEP and RSA1_5 key management expecting `*rsa.PublicKey` public key of corresponding length. + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + keyBytes,err := ioutil.ReadFile("public.key") + + if(err!=nil) { + panic("invalid key file") + } + + publicKey,e:=Rsa.ReadPublic(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + //OR: + //token,err := jose.Encrypt(payload, jose.RSA1_5, jose.A256GCM, publicKey) + token,err := jose.Encrypt(payload, jose.RSA_OAEP, jose.A256GCM, publicKey) + + if(err==nil) { + //go use token + fmt.Printf("\ntoken = %v\n",token) + } +} +``` + +#### AES Key Wrap key management family of algorithms +AES128KW, AES192KW and AES256KW key management requires `[]byte` array key of corresponding length + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} + + token,err := jose.Encrypt(payload,jose.A128KW,jose.A128GCM,sharedKey) + + if(err==nil) { + //go use token + fmt.Printf("\nA128KW A128GCM = %v\n",token) + } +} +``` + +#### AES GCM Key Wrap key management family of algorithms +AES128GCMKW, AES192GCMKW and AES256GCMKW key management requires `[]byte` array key of corresponding length + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} + + token,err := jose.Encrypt(payload,jose.A128GCMKW,jose.A128GCM,sharedKey) + + if(err==nil) { + //go use token + fmt.Printf("\nA128GCMKW A128GCM = %v\n",token) + } +} +``` + +#### ECDH-ES and ECDH-ES with AES Key Wrap key management family of algorithms +ECDH-ES and ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW key management requires `*ecdsa.PublicKey` elliptic curve key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*ecdsa.PublicKey` instance from PEM encoded PKCS1 X509 certificate or PKIX data: `ecc.ReadPublic([]byte)` or directly from `X,Y` parameters: `ecc.NewPublic(x,y []byte)`under `jose2go/keys/ecc` package: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello":"world"}` + + publicKey:=ecc.NewPublic([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}) + + token,err := jose.Encrypt(payload, jose.ECDH_ES, jose.A128CBC_HS256, publicKey) + + if(err==nil) { + //go use token + fmt.Printf("\ntoken = %v\n",token) + } +} +``` + +#### PBES2 using HMAC SHA with AES Key Wrap key management family of algorithms +PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW key management requires `string` passphrase from which actual key will be derived + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + passphrase := `top secret` + + token,err := jose.Encrypt(payload,jose.PBES2_HS256_A128KW,jose.A256GCM,passphrase) + + if(err==nil) { + //go use token + fmt.Printf("\nPBES2_HS256_A128KW A256GCM = %v\n",token) + } +} +``` + +#### DIR direct pre-shared symmetric key management +Direct key management with pre-shared symmetric keys expecting `[]byte` array key of corresponding length: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} + + token,err := jose.Encrypt(payload,jose.DIR,jose.A128GCM,sharedKey) + + if(err==nil) { + //go use token + fmt.Printf("\nDIR A128GCM = %v\n",token) + } +} +``` + +### Creating compressed & encrypted tokens +#### DEFLATE compression +**jose2go** supports optional DEFLATE compression of payload before encrypting, can be used with all supported encryption and key management algorithms: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := `{"hello": "world"}` + + sharedKey := []byte{194, 164, 235, 6, 138, 248, 171, 239, 24, 216, 11, 22, 137, 199, 215, 133} + + token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, sharedKey, jose.Zip(jose.DEF)) + + if err == nil { + //go use token + fmt.Printf("\nDIR A128GCM DEFLATED= %v\n", token) + } +} +``` + +### Verifying, Decoding and Decompressing tokens +Decoding json web tokens is fully symmetric to creating signed or encrypted tokens (with respect to public/private cryptography), decompressing deflated payloads is handled automatically: + +As of v1.2 decode method defined as `jose.Decode() payload string, headers map[string]interface{}, err error` and returns both payload as unprocessed string and headers as map. + +**HS256, HS384, HS512** signatures, **A128KW, A192KW, A256KW**,**A128GCMKW, A192GCMKW, A256GCMKW** and **DIR** key management algorithm expecting `[]byte` array key: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E" + + sharedKey :=[]byte{97,48,97,50,97,98,100,56,45,54,49,54,50,45,52,49,99,51,45,56,51,100,54,45,49,99,102,53,53,57,98,52,54,97,102,99} + + payload, headers, err := jose.Decode(token,sharedKey) + + if(err==nil) { + //go use token + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +**RS256, RS384, RS512**,**PS256, PS384, PS512** signatures expecting `*rsa.PublicKey` public key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*rsa.PublicKey` instance from PEM encoded PKCS1 X509 certificate or PKIX data: `Rsa.ReadPublic([]byte)` under `jose2go/keys/rsa` package: + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" + + keyBytes, err := ioutil.ReadFile("public.key") + + if(err!=nil) { + panic("invalid key file") + } + + publicKey, e:=Rsa.ReadPublic(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + payload, headers, err := jose.Decode(token, publicKey) + + if(err==nil) { + //go use token + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +**RSA-OAEP-256**, **RSA-OAEP** and **RSA1_5** key management algorithms expecting `*rsa.PrivateKey` private key of corresponding length: + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMjU2R0NNIn0.ixD3WVOkvaxeLKi0kyVqTzM6W2EW25SHHYCAr9473Xq528xSK0AVux6kUtv7QMkQKgkMvO8X4VdvonyGkDZTK2jgYUiI06dz7I1sjWJIbyNVrANbBsmBiwikwB-9DLEaKuM85Lwu6gnzbOF6B9R0428ckxmITCPDrzMaXwYZHh46FiSg9djChUTex0pHGhNDiEIgaINpsmqsOFX1L2Y7KM2ZR7wtpR3kidMV3JlxHdKheiPKnDx_eNcdoE-eogPbRGFdkhEE8Dyass1ZSxt4fP27NwsIer5pc0b922_3XWdi1r1TL_fLvGktHLvt6HK6IruXFHpU4x5Z2gTXWxEIog.zzTNmovBowdX2_hi.QSPSgXn0w25ugvzmu2TnhePn.0I3B9BE064HFNP2E0I7M9g" + + keyBytes, err := ioutil.ReadFile("private.key") + + if(err!=nil) { + panic("invalid key file") + } + + privateKey, e:=Rsa.ReadPrivate(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + payload, headers, err := jose.Decode(token, privateKey) + + if(err==nil) { + //go use payload + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +**PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW** key management algorithms expects `string` passpharase as a key + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := `eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJlZWpFZTF0YmJVbU5XV2s2In0.J2HTgltxH3p7A2zDgQWpZPgA2CHTSnDmMhlZWeSOMoZ0YvhphCeg-w.FzYG5AOptknu7jsG.L8jAxfxZhDNIqb0T96YWoznQ.yNeOfQWUbm8KuDGZ_5lL_g` + + passphrase := `top secret` + + payload, headers, err := jose.Decode(token,passphrase) + + if(err==nil) { + //go use token + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +**ES256, ES284, ES512** signatures expecting `*ecdsa.PublicKey` public elliptic curve key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*ecdsa.PublicKey` instance from PEM encoded PKCS1 X509 certificate or PKIX data: `ecc.ReadPublic([]byte)` or directly from `X,Y` parameters: `ecc.NewPublic(x,y []byte)`under `jose2go/keys/ecc` package: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" + + publicKey:=ecc.NewPublic([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}) + + payload, headers, err := jose.Decode(token, publicKey) + + if(err==nil) { + //go use token + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +**ECDH-ES** and **ECDH-ES+A128KW**, **ECDH-ES+A192KW**, **ECDH-ES+A256KW** key management expecting `*ecdsa.PrivateKey` private elliptic curve key of corresponding length. **jose2go** [provides convenient utils](#dealing-with-keys) to construct `*ecdsa.PrivateKey` instance from PEM encoded PKCS1 or PKCS8 data: `ecc.ReadPrivate([]byte)` or directly from `X,Y,D` parameters: `ecc.NewPrivate(x,y,d []byte)` under `jose2go/keys/ecc` package: + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiItVk1LTG5NeW9IVHRGUlpGNnFXNndkRm5BN21KQkdiNzk4V3FVMFV3QVhZIiwieSI6ImhQQWNReTgzVS01Qjl1U21xbnNXcFZzbHVoZGJSZE1nbnZ0cGdmNVhXTjgiLCJjcnYiOiJQLTI1NiJ9fQ..UA3N2j-TbYKKD361AxlXUA.XxFur_nY1GauVp5W_KO2DEHfof5s7kUwvOgghiNNNmnB4Vxj5j8VRS8vMOb51nYy2wqmBb2gBf1IHDcKZdACkCOMqMIcpBvhyqbuKiZPLHiilwSgVV6ubIV88X0vK0C8ZPe5lEyRudbgFjdlTnf8TmsvuAsdtPn9dXwDjUR23bD2ocp8UGAV0lKqKzpAw528vTfD0gwMG8gt_op8yZAxqqLLljMuZdTnjofAfsW2Rq3Z6GyLUlxR51DAUlQKi6UpsKMJoXTrm1Jw8sXBHpsRqA.UHCYOtnqk4SfhAknCnymaQ" + + privateKey:=ecc.NewPrivate([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}, + []byte{ 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 }) + + payload, headers, err := jose.Decode(token, privateKey) + + if(err==nil) { + //go use token + fmt.Printf("\npayload = %v\n",payload) + + //and/or use headers + fmt.Printf("\nheaders = %v\n",headers) + } +} +``` + +### Adding extra headers +It's possible to pass additional headers while encoding token. **jose2go** provides convenience configuration helpers: `Header(name string, value interface{})` and `Headers(headers map[string]interface{})` that can be passed to `Sign(..)` and `Encrypt(..)` calls. + +Note: **jose2go** do not allow to override `alg`, `enc` and `zip` headers. + +Example of signing with extra headers: +```Go + token, err := jose.Sign(payload, jose.ES256, key, + jose.Header("keyid", "111-222-333"), + jose.Header("trans-id", "aaa-bbb")) +``` + +Encryption with extra headers: +```Go +token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, sharedKey, + jose.Headers(map[string]interface{}{"keyid": "111-22-33", "cty": "text/plain"})) +``` + +### Two phase validation +In some cases validation (decoding) key can be unknown prior to examining token content. For instance one can use different keys per token issuer or rely on headers information to determine which key to use, do logging or other things. + +**jose2go** allows to pass `func(headers map[string]interface{}, payload string) key interface{}` callback instead of key to `jose.Decode(..)`. Callback will be executed prior to decoding and integrity validation and will recieve parsed headers and payload as is (for encrypted tokens it will be cipher text). Callback should return key to be used for actual decoding process or `error` if decoding should be stopped, given error object will be returned from `jose.Decode(..)` call. + +Example of decoding token with callback: + +```Go +package main + +import ( + "crypto/rsa" + "fmt" + "github.com/dvsekhvalnov/jose2go" + "github.com/dvsekhvalnov/jose2go/keys/rsa" + "io/ioutil" + "errors" +) + +func main() { + + token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" + + payload, _, err := jose.Decode(token, + func(headers map[string]interface{}, payload string) interface{} { + //log something + fmt.Printf("\nHeaders before decoding: %v\n", headers) + fmt.Printf("\nPayload before decoding: %v\n", payload) + + //lookup key based on keyid header as en example + //or lookup based on something from payload, e.g. 'iss' claim for instance + key := FindKey(headers['keyid']) + + if(key==nil) { + return errors.New("Key not found") + } + + return key; + }) + + if err == nil { + //go use token + fmt.Printf("\ndecoded payload = %v\n", payload) + } +} +``` + +Two phase validation can be used for implementing additional things like strict `alg` or `enc` validation, see [Customizing library for security](#customizing-library-for-security) for more information. + +### Working with binary payload +In addition to work with string payloads (typical use-case) `jose2go` supports +encoding and decoding of raw binary data. `jose.DecodeBytes`, `jose.SignBytes` +and `jose.EncryptBytes` functions provides similar interface but accepting +`[]byte` payloads. + +Examples: + +```Go +package main + +import ( + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + token := `eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJlZWpFZTF0YmJVbU5XV2s2In0.J2HTgltxH3p7A2zDgQWpZPgA2CHTSnDmMhlZWeSOMoZ0YvhphCeg-w.FzYG5AOptknu7jsG.L8jAxfxZhDNIqb0T96YWoznQ.yNeOfQWUbm8KuDGZ_5lL_g` + + passphrase := `top secret` + + payload, headers, err := jose.DecodeBytes(token,passphrase) + + if(err==nil) { + //go use token + //payload = []byte{....} + } +} +``` + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := []byte {0x01, 0x02, 0x03, 0x04} + + keyBytes,err := ioutil.ReadFile("private.key") + + if(err!=nil) { + panic("invalid key file") + } + + privateKey,e:=Rsa.ReadPrivate(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + token,err := jose.SignBytes(payload,jose.RS256, privateKey) + + if(err==nil) { + //go use token + fmt.Printf("\nRS256 = %v\n",token) + } +} +``` + +```Go +package main + +import ( + "fmt" + "io/ioutil" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "github.com/dvsekhvalnov/jose2go" +) + +func main() { + + payload := []byte {0x01, 0x02, 0x03, 0x04} + + keyBytes,err := ioutil.ReadFile("public.key") + + if(err!=nil) { + panic("invalid key file") + } + + publicKey,e:=Rsa.ReadPublic(keyBytes) + + if(e!=nil) { + panic("invalid key format") + } + + token,err := jose.EncryptBytes(payload, jose.RSA_OAEP, jose.A256GCM, publicKey) + + if(err==nil) { + //go use token + fmt.Printf("\ntoken = %v\n",token) + } +} +``` +### Dealing with keys +**jose2go** provides several helper methods to simplify loading & importing of elliptic and rsa keys. Import `jose2go/keys/rsa` or `jose2go/keys/ecc` respectively: + +#### RSA keys +1. `Rsa.ReadPrivate(raw []byte) (key *rsa.PrivateKey,err error)` attempts to parse RSA private key from PKCS1 or PKCS8 format (`BEGIN RSA PRIVATE KEY` and `BEGIN PRIVATE KEY` headers) + +```Go +package main + +import ( + "fmt" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "io/ioutil" +) + +func main() { + + keyBytes, _ := ioutil.ReadFile("private.key") + + privateKey, err:=Rsa.ReadPrivate(keyBytes) + + if(err!=nil) { + panic("invalid key format") + } + + fmt.Printf("privateKey = %v\n",privateKey) +} +``` + +2. `Rsa.ReadPublic(raw []byte) (key *rsa.PublicKey,err error)` attempts to parse RSA public key from PKIX key format or PKCS1 X509 certificate (`BEGIN PUBLIC KEY` and `BEGIN CERTIFICATE` headers) + +```Go +package main + +import ( + "fmt" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" + "io/ioutil" +) + +func main() { + + keyBytes, _ := ioutil.ReadFile("public.cer") + + publicKey, err:=Rsa.ReadPublic(keyBytes) + + if(err!=nil) { + panic("invalid key format") + } + + fmt.Printf("publicKey = %v\n",publicKey) +} +``` + +#### ECC keys +1. `ecc.ReadPrivate(raw []byte) (key *ecdsa.PrivateKey,err error)` attemps to parse elliptic curve private key from PKCS1 or PKCS8 format (`BEGIN EC PRIVATE KEY` and `BEGIN PRIVATE KEY` headers) + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "io/ioutil" +) + +func main() { + + keyBytes, _ := ioutil.ReadFile("ec-private.pem") + + ecPrivKey, err:=ecc.ReadPrivate(keyBytes) + + if(err!=nil) { + panic("invalid key format") + } + + fmt.Printf("ecPrivKey = %v\n",ecPrivKey) +} +``` + +2. `ecc.ReadPublic(raw []byte) (key *ecdsa.PublicKey,err error)` attemps to parse elliptic curve public key from PKCS1 X509 or PKIX format (`BEGIN PUBLIC KEY` and `BEGIN CERTIFICATE` headers) + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "io/ioutil" +) + +func main() { + + keyBytes, _ := ioutil.ReadFile("ec-public.key") + + ecPubKey, err:=ecc.ReadPublic(keyBytes) + + if(err!=nil) { + panic("invalid key format") + } + + fmt.Printf("ecPubKey = %v\n",ecPubKey) +} +``` + +3. `ecc.NewPublic(x,y []byte) (*ecdsa.PublicKey)` constructs elliptic public key from (X,Y) represented as bytes. Supported are NIST curves P-256,P-384 and P-521. Curve detected automatically by input length. + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" +) + +func main() { + + ecPubKey:=ecc.NewPublic([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}) + + fmt.Printf("ecPubKey = %v\n",ecPubKey) +} +``` + +4. `ecc.NewPrivate(x,y,d []byte) (*ecdsa.PrivateKey)` constructs elliptic private key from (X,Y) and D represented as bytes. Supported are NIST curves P-256,P-384 and P-521. Curve detected automatically by input length. + +```Go +package main + +import ( + "fmt" + "github.com/dvsekhvalnov/jose2go/keys/ecc" +) + +func main() { + + ecPrivKey:=ecc.NewPrivate([]byte{4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9}, + []byte{131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53}, + []byte{ 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 }) + + fmt.Printf("ecPrivKey = %v\n",ecPrivKey) +} +``` + +### More examples +Checkout `jose_test.go` for more examples. + +## Customizing library for security +In response to ever increasing attacks on various JWT implementations, `jose2go` as of version v1.6 introduced number of additional security controls to limit potential attack surface on services and projects using the library. + +### Deregister algorithm implementations +One can use following methods to deregister any signing, encryption, key management or compression algorithms from runtime suite, that is considered unsafe or simply not expected by service. + +- `func DeregisterJwa(alg string) JwaAlgorithm` +- `func DeregisterJwe(alg string) JweEncryption` +- `func DeregisterJws(alg string) JwsAlgorithm` +- `func DeregisterJwc(alg string) JwcAlgorithm` + +All of them expecting alg name matching `jose` constants and returns implementation that have been deregistered. + +### Strict validation +Sometimes it is desirable to verify that `alg` or `enc` values are matching expected before attempting to decode actual payload. +`jose2go` provides helper matchers to be used within [Two-phase validation](#two-phase-validation) precheck: + +- `jose.Alg(key, alg)` - to match alg header +- `jose.Enc(key, alg)` - to match alg and enc headers + +```Go + token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" + + key := Rsa.ReadPublic(....) + + // we expecting 'RS256' alg here and if matching continue to decode with a key + payload, header, err := jose.Decode(token, Alg(key, "RS256")) + + // or match both alg and enc for decrypting scenarios + payload, header, err := jose.Decode(token, Enc(key, "RSA-OAEP-256", "A192CBC-HS384")) +``` + +### Customizing PBKDF2 +As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v1.6 to reduce runtime exposure. + +By default, maxIterations is set according to [OWASP PBKDF2](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2) Recomendations: + +``` +PBES2-HS256+A128KW: 1300000 +PBES2-HS384+A192KW: 950000 +PBES2-HS512+A256KW: 600000 +``` + +, while minIterations kept at 0 for backward compatibility. + +If it is desired to implement different limits, register new implementation with new parameters: + +```Go + jose.RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 1300000)) + jose.RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 950000)) + jose.RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 600000)) +``` + +In case you can't upgrade to latest version, but would like to have protections against PBES2 abuse, it is recommended to stick with [Two-phase validation](#two-phase-validation) precheck before decoding: + +```Go +test, headers, err := Decode(token, func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + p2c := headers["p2c"].(float64) + + if strings.HasPrefix(alg, "PBES2-") && int64(p2c) > 100 { + return errors.New("Too many p2c interation count, aborting") + } + + return "top secret" +}) +``` + +## Changelog +### 1.6 +- ability to deregister specific algorithms +- configurable min/max restrictions for PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW + +### 1.5 +- security and bug fixes + +### 1.4 +- removed extra headers to be inserted by library + +### 1.3 +- security fixes: Invalid Curve Attack on NIST curves + +### 1.2 +- interface to access token headers after decoding +- interface to provide extra headers for token encoding +- two-phase validation support + +### 1.1 +- security and bug fixes + +### 1.0 +- initial stable version with full suite JOSE spec support diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aes/ecb.go b/vendor/github.com/dvsekhvalnov/jose2go/aes/ecb.go new file mode 100644 index 0000000..ec9af99 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aes/ecb.go @@ -0,0 +1,68 @@ +// Package aes contains provides AES Key Wrap and ECB mode implementations +package aes + +import ( + "crypto/cipher" +) + +type ecb struct { + b cipher.Block +} + +type ecbEncrypter ecb +type ecbDecrypter ecb + +// NewECBEncrypter creates BlockMode for AES encryption in ECB mode +func NewECBEncrypter(b cipher.Block) cipher.BlockMode { + return &ecbEncrypter{b: b} +} + +// NewECBDecrypter creates BlockMode for AES decryption in ECB mode +func NewECBDecrypter(b cipher.Block) cipher.BlockMode { + return &ecbDecrypter{b: b} +} + +func (x *ecbEncrypter) BlockSize() int { return x.b.BlockSize() } +func (x *ecbDecrypter) BlockSize() int { return x.b.BlockSize() } + +func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { + bs := x.BlockSize() + + if len(src)%bs != 0 { + panic("ecbDecrypter.CryptBlocks(): input not full blocks") + } + + if len(dst) < len(src) { + panic("ecbDecrypter.CryptBlocks(): output smaller than input") + } + + if len(src) == 0 { + return + } + + for len(src) > 0 { + x.b.Decrypt(dst, src) + src = src[bs:] + } +} + +func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { + bs := x.BlockSize() + + if len(src)%bs != 0 { + panic("ecbEncrypter.CryptBlocks(): input not full blocks") + } + + if len(dst) < len(src) { + panic("ecbEncrypter.CryptBlocks(): output smaller than input") + } + + if len(src) == 0 { + return + } + + for len(src) > 0 { + x.b.Encrypt(dst, src) + src = src[bs:] + } +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aes/key_wrap.go b/vendor/github.com/dvsekhvalnov/jose2go/aes/key_wrap.go new file mode 100644 index 0000000..8e5963e --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aes/key_wrap.go @@ -0,0 +1,113 @@ +package aes + +import ( + "github.com/dvsekhvalnov/jose2go/arrays" + "crypto/cipher" + "crypto/aes" + "crypto/hmac" + "errors" +) + +var defaultIV=[]byte { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 } + +// KeyWrap encrypts provided key (CEK) with KEK key using AES Key Wrap (rfc 3394) algorithm +func KeyWrap(cek,kek []byte) ([]byte,error) { + // 1) Initialize variables + a := defaultIV // Set A = IV, an initial value + r := arrays.Slice(cek, 8) // For i = 1 to n + // R[0][i] = P[i] + n := uint64(len(r)) + + // 2) Calculate intermediate values. + var j,i,t uint64 + + for j = 0; j < 6; j++ { // For j = 0 to 5 + for i = 0; i < n; i++ { // For i=1 to n + t = n * j + i + 1; + b,e := aesEnc(kek, arrays.Concat(a, r[i])) // B=AES(K, A | R[i]) + + if e!=nil { return nil, e } + + a = b[:len(b)/2] // A=MSB(64,B) ^ t where t = (n*j)+i + r[i] = b[len(b)/2:] // R[i] = LSB(64, B) + a = arrays.Xor(a, arrays.UInt64ToBytes(t)) + } + } + + // 3) Output the results + c := make([][]byte, n+1, n+1) + c[0] = a; // Set C[0] = A + for i = 1; i <= n; i++ { // For i = 1 to n + c[i] = r[i - 1] // C[i] = R[i] + } + + return arrays.Unwrap(c),nil +} + +// KeyUnwrap decrypts previously encrypted key (CEK) with KEK key using AES Key Wrap (rfc 3394) algorithm +func KeyUnwrap(encryptedCek, kek []byte) ([]byte,error) { + // 1) Initialize variables + c := arrays.Slice(encryptedCek, 8); + a := c[0]; // Set A = C[0] + r := make([][]byte,len(c) - 1); + + for i := 1; i < len(c); i++ { // For i = 1 to n + r[i - 1] = c[i]; // R[i] = C[i] + } + + n := uint64(len(r)) + + // 2) Calculate intermediate values + var t,j uint64 + + for j = 6; j > 0; j-- { // For j = 5 to 0 + for i := n; i > 0; i-- { // For i = n to 1 + t = n * (j-1) + i; + a = arrays.Xor(a, arrays.UInt64ToBytes(t)) + b,e := aesDec(kek, arrays.Concat(a, r[i-1])) // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + + if e!=nil { return nil,e } + + a = b[:len(b)/2] // A = MSB(64, B) + r[i-1] = b[len(b)/2:] // R[i] = LSB(64, B) + } + } + + // 3) Output the results + if (!hmac.Equal(defaultIV, a)) { // If A is an appropriate initial value + return nil, errors.New("aes.KeyUnwrap(): integrity check failed.") + } + + // For i = 1 to n + return arrays.Unwrap(r),nil // P[i] = R[i] + +} + +func aesEnc(kek, plainText []byte) (cipherText []byte, err error) { + var block cipher.Block + + if block, err = aes.NewCipher(kek);err!=nil { + return nil,err + } + + cipherText = make([]byte, len(plainText)) + + NewECBEncrypter(block).CryptBlocks(cipherText,plainText) + + return cipherText,nil +} + +func aesDec(kek, cipherText []byte) (plainText []byte,err error) { + + var block cipher.Block + + if block, err = aes.NewCipher(kek);err!=nil { + return nil,err + } + + plainText = make([]byte, len(cipherText)) + + NewECBDecrypter(block).CryptBlocks(plainText,cipherText) + + return plainText,nil +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aes_cbc_hmac.go b/vendor/github.com/dvsekhvalnov/jose2go/aes_cbc_hmac.go new file mode 100644 index 0000000..a2217e1 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aes_cbc_hmac.go @@ -0,0 +1,112 @@ +package jose + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "errors" + "fmt" + "github.com/dvsekhvalnov/jose2go/arrays" + "github.com/dvsekhvalnov/jose2go/padding" +) + +// AES CBC with HMAC authenticated encryption algorithm implementation +type AesCbcHmac struct { + keySizeBits int +} + +func init() { + RegisterJwe(&AesCbcHmac{keySizeBits: 256}) + RegisterJwe(&AesCbcHmac{keySizeBits: 384}) + RegisterJwe(&AesCbcHmac{keySizeBits: 512}) +} + +func (alg *AesCbcHmac) Name() string { + switch alg.keySizeBits { + case 256: + return A128CBC_HS256 + case 384: + return A192CBC_HS384 + default: + return A256CBC_HS512 + } +} + +func (alg *AesCbcHmac) KeySizeBits() int { + return alg.keySizeBits +} + +func (alg *AesCbcHmac) SetKeySizeBits(bits int) { + alg.keySizeBits = bits +} + +func (alg *AesCbcHmac) Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) { + + cekSizeBits := len(cek) << 3 + if cekSizeBits != alg.keySizeBits { + return nil, nil, nil, errors.New(fmt.Sprintf("AesCbcHmac.Encrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) + } + + hmacKey := cek[0 : len(cek)/2] + aesKey := cek[len(cek)/2:] + + if iv, err = arrays.Random(16); err != nil { + return nil, nil, nil, err + } + + var block cipher.Block + + if block, err = aes.NewCipher(aesKey); err != nil { + return nil, nil, nil, err + } + + padded := padding.AddPkcs7(plainText, 16) + + cipherText = make([]byte, len(padded), cap(padded)) + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(cipherText, padded) + + authTag = alg.computeAuthTag(aad, iv, cipherText, hmacKey) + + return iv, cipherText, authTag, nil +} + +func (alg *AesCbcHmac) Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) { + + cekSizeBits := len(cek) << 3 + + if cekSizeBits != alg.keySizeBits { + return nil, errors.New(fmt.Sprintf("AesCbcHmac.Decrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) + } + + hmacKey := cek[0 : len(cek)/2] + aesKey := cek[len(cek)/2:] + + // Check MAC + expectedAuthTag := alg.computeAuthTag(aad, iv, cipherText, hmacKey) + + if !hmac.Equal(expectedAuthTag, authTag) { + return nil, errors.New("AesCbcHmac.Decrypt(): Authentication tag do not match.") + } + + var block cipher.Block + + if block, err = aes.NewCipher(aesKey); err == nil { + mode := cipher.NewCBCDecrypter(block, iv) + + var padded []byte = make([]byte, len(cipherText), cap(cipherText)) + mode.CryptBlocks(padded, cipherText) + + return padding.RemovePkcs7(padded, 16), nil + } + + return nil, err +} + +func (alg *AesCbcHmac) computeAuthTag(aad []byte, iv []byte, cipherText []byte, hmacKey []byte) (signature []byte) { + al := arrays.UInt64ToBytes(uint64(len(aad) << 3)) + hmacInput := arrays.Concat(aad, iv, cipherText, al) + hmac := calculateHmac(alg.keySizeBits, hmacInput, hmacKey) + + return hmac[0 : len(hmac)/2] +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm.go b/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm.go new file mode 100644 index 0000000..57fc861 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm.go @@ -0,0 +1,98 @@ +package jose + +import ( + "fmt" + "errors" + "crypto/aes" + "crypto/cipher" + "github.com/dvsekhvalnov/jose2go/arrays" +) + +// AES GCM authenticated encryption algorithm implementation +type AesGcm struct{ + keySizeBits int +} + +func init() { + RegisterJwe(&AesGcm{keySizeBits:128}) + RegisterJwe(&AesGcm{keySizeBits:192}) + RegisterJwe(&AesGcm{keySizeBits:256}) +} + +func (alg *AesGcm) Name() string { + switch alg.keySizeBits { + case 128: return A128GCM + case 192: return A192GCM + default: return A256GCM + } +} + +func (alg *AesGcm) KeySizeBits() int { + return alg.keySizeBits +} + +func (alg *AesGcm) Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) { + + cekSizeBits := len(cek)<<3 + + if cekSizeBits != alg.keySizeBits { + return nil,nil,nil, errors.New(fmt.Sprintf("AesGcm.Encrypt(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, cekSizeBits)) + } + + if iv,err = arrays.Random(12);err!=nil { + return nil,nil,nil,err + } + + var block cipher.Block + + if block, err = aes.NewCipher(cek);err!=nil { + return nil,nil,nil,err + } + + var aesgcm cipher.AEAD + + if aesgcm,err = cipher.NewGCM(block);err!=nil { + return nil,nil,nil,err + } + + cipherWithTag := aesgcm.Seal(nil, iv, plainText, aad) + + cipherText=cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] + authTag=cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] + + return iv, cipherText, authTag, nil +} + +func (alg *AesGcm) Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) { + + cekSizeBits := len(cek)<<3 + + if cekSizeBits != alg.keySizeBits { + return nil, errors.New(fmt.Sprintf("AesGcm.Decrypt(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, cekSizeBits)) + } + + var block cipher.Block + + if block, err = aes.NewCipher(cek);err!=nil { + return nil,err + } + + var aesgcm cipher.AEAD + + if aesgcm,err = cipher.NewGCM(block);err!=nil { + return nil,err + } + + cipherWithTag:=append(cipherText,authTag...) + + if nonceSize := len(iv); nonceSize != aesgcm.NonceSize() { + return nil, errors.New(fmt.Sprintf("AesGcm.Decrypt(): expected nonce of size %v bits, but was given %v bits.", aesgcm.NonceSize()<<3, nonceSize<<3)) + } + + if plainText,err = aesgcm.Open(nil, iv, cipherWithTag, aad);err!=nil { + return nil,err + } + + return plainText,nil +} + diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm_kw.go b/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm_kw.go new file mode 100644 index 0000000..e6f5681 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aes_gcm_kw.go @@ -0,0 +1,128 @@ +package jose + +import ( + "errors" + "fmt" + "github.com/dvsekhvalnov/jose2go/base64url" + "github.com/dvsekhvalnov/jose2go/arrays" + "crypto/aes" + "crypto/cipher" +) + +func init() { + RegisterJwa(&AesGcmKW{ keySizeBits: 128}) + RegisterJwa(&AesGcmKW{ keySizeBits: 192}) + RegisterJwa(&AesGcmKW{ keySizeBits: 256}) +} + +// AES GCM Key Wrap key management algorithm implementation +type AesGcmKW struct { + keySizeBits int +} + +func (alg *AesGcmKW) Name() string { + switch alg.keySizeBits { + case 128: return A128GCMKW + case 192: return A192GCMKW + default: return A256GCMKW + } +} + +func (alg *AesGcmKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if kek,ok:=key.([]byte); ok { + + kekSizeBits := len(kek) << 3 + + if kekSizeBits != alg.keySizeBits { + return nil,nil, errors.New(fmt.Sprintf("AesGcmKW.WrapNewKey(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, kekSizeBits)) + } + + if cek,err = arrays.Random(cekSizeBits>>3);err!=nil { + return nil,nil,err + } + + var iv []byte + + if iv,err = arrays.Random(12);err!=nil { + return nil,nil,err + } + + var block cipher.Block + + if block, err = aes.NewCipher(kek);err!=nil { + return nil,nil,err + } + + var aesgcm cipher.AEAD + + if aesgcm,err = cipher.NewGCM(block);err!=nil { + return nil,nil,err + } + + cipherWithTag := aesgcm.Seal(nil, iv, cek, nil) + + cipherText := cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] + authTag := cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] + + header["iv"]=base64url.Encode(iv) + header["tag"]=base64url.Encode(authTag) + + return cek,cipherText,nil + } + + return nil,nil,errors.New("AesGcmKW.WrapNewKey(): expected key to be '[]byte' array") +} + +func (alg *AesGcmKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + if kek,ok:=key.([]byte); ok { + + kekSizeBits := len(kek) << 3 + + if kekSizeBits != alg.keySizeBits { + return nil,errors.New(fmt.Sprintf("AesGcmKW.Unwrap(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) + } + + var iv,tag string + + if iv,ok = header["iv"].(string);!ok { + return nil,errors.New("AesGcmKW.Unwrap(): expected 'iv' param in JWT header, but was not found.") + } + + if tag,ok = header["tag"].(string);!ok { + return nil,errors.New("AesGcmKW.Unwrap(): expected 'tag' param in JWT header, but was not found.") + } + + var ivBytes,tagBytes []byte + + if ivBytes,err = base64url.Decode(iv);err!=nil { + return nil,err + } + + if tagBytes,err = base64url.Decode(tag);err!=nil { + return nil,err + } + + var block cipher.Block + + if block, err = aes.NewCipher(kek);err!=nil { + return nil,err + } + + var aesgcm cipher.AEAD + + if aesgcm,err = cipher.NewGCM(block);err!=nil { + return nil,err + } + + cipherAndTag:=append(encryptedCek,tagBytes...) + + if cek,err = aesgcm.Open(nil, ivBytes,cipherAndTag , nil);err!=nil { + fmt.Printf("err = %v\n",err) + return nil,err + } + + return cek,nil + } + + return nil,errors.New("AesGcmKW.Unwrap(): expected key to be '[]byte' array") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/aeskw.go b/vendor/github.com/dvsekhvalnov/jose2go/aeskw.go new file mode 100644 index 0000000..c5b3e4f --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/aeskw.go @@ -0,0 +1,64 @@ +package jose + +import ( + "errors" + "fmt" + "github.com/dvsekhvalnov/jose2go/aes" + "github.com/dvsekhvalnov/jose2go/arrays" +) + +func init() { + RegisterJwa(&AesKW{ keySizeBits: 128}) + RegisterJwa(&AesKW{ keySizeBits: 192}) + RegisterJwa(&AesKW{ keySizeBits: 256}) +} + +// AES Key Wrap key management algorithm implementation +type AesKW struct { + keySizeBits int +} + +func (alg *AesKW) Name() string { + switch alg.keySizeBits { + case 128: return A128KW + case 192: return A192KW + default: return A256KW + } +} + +func (alg *AesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if kek,ok:=key.([]byte); ok { + + kekSizeBits := len(kek) << 3 + + if kekSizeBits != alg.keySizeBits { + return nil,nil, errors.New(fmt.Sprintf("AesKW.WrapNewKey(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, kekSizeBits)) + } + + if cek,err = arrays.Random(cekSizeBits>>3);err==nil { + encryptedCek,err=aes.KeyWrap(cek,kek) + return + } + + return nil,nil,err + + } + + return nil,nil,errors.New("AesKW.WrapNewKey(): expected key to be '[]byte' array") +} + +func (alg *AesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + + if kek,ok:=key.([]byte); ok { + + kekSizeBits := len(kek) << 3 + + if kekSizeBits != alg.keySizeBits { + return nil,errors.New(fmt.Sprintf("AesKW.Unwrap(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) + } + + return aes.KeyUnwrap(encryptedCek, kek) + } + + return nil,errors.New("AesKW.Unwrap(): expected key to be '[]byte' array") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/arrays/arrays.go b/vendor/github.com/dvsekhvalnov/jose2go/arrays/arrays.go new file mode 100644 index 0000000..17ff0bd --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/arrays/arrays.go @@ -0,0 +1,116 @@ +// Package arrays provides various byte array utilities +package arrays + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "fmt" + "github.com/dvsekhvalnov/jose2go/base64url" +) + +// Xor is doing byte by byte exclusive or of 2 byte arrays +func Xor(left, right []byte) []byte { + result := make([]byte, len(left)) + + for i := 0; i < len(left); i++ { + result[i] = left[i] ^ right[i] + } + + return result +} + +// Slice is splitting input byte array into slice of subarrays. Each of count length. +func Slice(arr []byte, count int) [][]byte { + + sliceCount := len(arr) / count + result := make([][]byte, sliceCount) + + for i := 0; i < sliceCount; i++ { + start := i * count + end := i*count + count + + result[i] = arr[start:end] + } + + return result +} + +// Random generates byte array with random data of byteCount length +func Random(byteCount int) ([]byte, error) { + data := make([]byte, byteCount) + + if _, err := rand.Read(data); err != nil { + return nil, err + } + + return data, nil +} + +// Concat combine several arrays into single one, resulting slice = A1 | A2 | A3 | ... | An +func Concat(arrays ...[]byte) []byte { + var result []byte = arrays[0] + + for _, arr := range arrays[1:] { + result = append(result, arr...) + } + + return result +} + +// Unwrap same thing as Contact, just different interface, combines several array into single one +func Unwrap(arrays [][]byte) []byte { + var result []byte = arrays[0] + + for _, arr := range arrays[1:] { + result = append(result, arr...) + } + + return result +} + +// UInt64ToBytes unwrap uint64 value to byte array of length 8 using big endian +func UInt64ToBytes(value uint64) []byte { + result := make([]byte, 8) + binary.BigEndian.PutUint64(result, value) + + return result +} + +// UInt32ToBytes unwrap uint32 value to byte array of length 4 using big endian +func UInt32ToBytes(value uint32) []byte { + result := make([]byte, 4) + binary.BigEndian.PutUint32(result, value) + + return result +} + +// Dump produces printable debug representation of byte array as string +func Dump(arr []byte) string { + var buf bytes.Buffer + + buf.WriteString("(") + buf.WriteString(fmt.Sprintf("%v", len(arr))) + buf.WriteString(" bytes)[") + + for idx, b := range arr { + buf.WriteString(fmt.Sprintf("%v", b)) + if idx != len(arr)-1 { + buf.WriteString(", ") + } + } + + buf.WriteString("], Hex: [") + + for idx, b := range arr { + buf.WriteString(fmt.Sprintf("%X", b)) + if idx != len(arr)-1 { + buf.WriteString(" ") + } + } + + buf.WriteString("], Base64Url:") + buf.WriteString(base64url.Encode(arr)) + + return buf.String() +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/base64url/base64url.go b/vendor/github.com/dvsekhvalnov/jose2go/base64url/base64url.go new file mode 100644 index 0000000..7229a85 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/base64url/base64url.go @@ -0,0 +1,31 @@ +// package base64url provides base64url encoding/decoding support +package base64url + +import ( + "strings" + "encoding/base64" +) + +// Decode decodes base64url string to byte array +func Decode(data string) ([]byte,error) { + data = strings.Replace(data, "-", "+", -1) // 62nd char of encoding + data = strings.Replace(data, "_", "/", -1) // 63rd char of encoding + + switch(len(data) % 4) { // Pad with trailing '='s + case 0: // no padding + case 2: data+="==" // 2 pad chars + case 3: data+="=" // 1 pad char + } + + return base64.StdEncoding.DecodeString(data) +} + +// Encode encodes given byte array to base64url string +func Encode(data []byte) string { + result := base64.StdEncoding.EncodeToString(data) + result = strings.Replace(result, "+", "-", -1) // 62nd char of encoding + result = strings.Replace(result, "/", "_", -1) // 63rd char of encoding + result = strings.Replace(result, "=", "", -1) // Remove any trailing '='s + + return result +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/compact/compact.go b/vendor/github.com/dvsekhvalnov/jose2go/compact/compact.go new file mode 100644 index 0000000..1e9dd2f --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/compact/compact.go @@ -0,0 +1,33 @@ +// package compact provides function to work with json compact serialization format +package compact + +import ( + "github.com/dvsekhvalnov/jose2go/base64url" + "strings" +) + +// Parse splitting & decoding compact serialized json web token, returns slice of byte arrays, each representing part of token +func Parse(token string) (result [][]byte, e error) { + parts := strings.Split(token, ".") + + result = make([][]byte, len(parts)) + + for i, part := range parts { + if result[i], e = base64url.Decode(part); e != nil { + return nil, e + } + } + + return result, nil +} + +// Serialize converts given parts into compact serialization format +func Serialize(parts ...[]byte) string { + result := make([]string, len(parts)) + + for i, part := range parts { + result[i] = base64url.Encode(part) + } + + return strings.Join(result, ".") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/deflate.go b/vendor/github.com/dvsekhvalnov/jose2go/deflate.go new file mode 100644 index 0000000..c788f5b --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/deflate.go @@ -0,0 +1,39 @@ +package jose + +import ( + "bytes" + "compress/flate" + "io/ioutil" +) + +func init() { + RegisterJwc(new(Deflate)) +} + +// Deflate compression algorithm implementation +type Deflate struct {} + +func (alg *Deflate) Name() string { + return DEF +} + +func (alg *Deflate) Compress(plainText []byte) []byte { + var buf bytes.Buffer + deflate,_ := flate.NewWriter(&buf, 8) //level=DEFLATED + + deflate.Write(plainText) + deflate.Close() + + return buf.Bytes() +} + +func (alg *Deflate) Decompress(compressedText []byte) []byte { + + enflated,_ := ioutil.ReadAll( + flate.NewReader( + bytes.NewReader(compressedText))) + + return enflated +} + + diff --git a/vendor/github.com/dvsekhvalnov/jose2go/direct.go b/vendor/github.com/dvsekhvalnov/jose2go/direct.go new file mode 100644 index 0000000..51f0a2f --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/direct.go @@ -0,0 +1,39 @@ +package jose + +import ( + "errors" +) + +func init() { + RegisterJwa(new(Direct)) +} + +// Direct (pre-shared) key management algorithm implementation +type Direct struct{ +} + +func (alg *Direct) Name() string { + return DIR +} + +func (alg *Direct) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + + if cek,ok:=key.([]byte); ok { + return cek,[]byte{},nil + } + + return nil,nil,errors.New("Direct.WrapNewKey(): expected key to be '[]byte' array") +} + +func (alg *Direct) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + + if(len(encryptedCek)!=0) { + return nil, errors.New("Direct.Unwrap(): expected empty encrypted CEK") + } + + if cek,ok:=key.([]byte); ok { + return cek,nil + } + + return nil,errors.New("Direct.Unwrap(): expected key to be '[]byte' array") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/ecdh.go b/vendor/github.com/dvsekhvalnov/jose2go/ecdh.go new file mode 100644 index 0000000..c3512fb --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/ecdh.go @@ -0,0 +1,157 @@ +package jose + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + "github.com/dvsekhvalnov/jose2go/arrays" + "github.com/dvsekhvalnov/jose2go/base64url" + "github.com/dvsekhvalnov/jose2go/kdf" + "github.com/dvsekhvalnov/jose2go/keys/ecc" + "github.com/dvsekhvalnov/jose2go/padding" + "math/big" +) + +func init() { + RegisterJwa(&Ecdh{directAgreement: true}) +} + +// Elliptic curve Diffie–Hellman key management (key agreement) algorithm implementation +type Ecdh struct { + directAgreement bool +} + +func (alg *Ecdh) Name() string { + return ECDH_ES +} + +func (alg *Ecdh) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + + if pubKey, ok := key.(*ecdsa.PublicKey); ok { + + if _, ok := header[alg.idHeader()].(string); !ok { + return nil, nil, errors.New(fmt.Sprintf("Ecdh.WrapNewKey(): expected '%v' param in JWT header, but was not found.", alg.idHeader())) + } + + var d []byte + var x, y *big.Int + + if d, x, y, err = elliptic.GenerateKey(pubKey.Curve, rand.Reader); err != nil { + return nil, nil, err + } + + ephemeral := ecc.NewPrivate(x.Bytes(), y.Bytes(), d) + + xBytes := padding.Align(x.Bytes(), pubKey.Curve.Params().BitSize) + yBytes := padding.Align(y.Bytes(), pubKey.Curve.Params().BitSize) + + epk := map[string]string{ + "kty": "EC", + "x": base64url.Encode(xBytes), + "y": base64url.Encode(yBytes), + "crv": name(pubKey.Curve), + } + + header["epk"] = epk + + return alg.deriveKey(pubKey, ephemeral, cekSizeBits, header), nil, nil + } + + return nil, nil, errors.New("Ecdh.WrapNewKey(): expected key to be '*ecdsa.PublicKey'") +} + +func (alg *Ecdh) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + + if privKey, ok := key.(*ecdsa.PrivateKey); ok { + + var epk map[string]interface{} + + if epk, ok = header["epk"].(map[string]interface{}); !ok { + return nil, errors.New("Ecdh.Unwrap(): expected 'epk' param in JWT header, but was not found.") + } + + if _, ok := header[alg.idHeader()].(string); !ok { + return nil, errors.New(fmt.Sprintf("Ecdh.Unwrap(): expected '%v' param in JWT header, but was not found.", alg.idHeader())) + } + + var x, y, crv string + var xBytes, yBytes []byte + + if x, ok = epk["x"].(string); !ok { + return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'x' was not found.") + } + + if y, ok = epk["y"].(string); !ok { + return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'y' was not found.") + } + + if crv, ok = epk["crv"].(string); !ok { + return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'crv' was not found.") + } + + if crv != "P-256" && crv != "P-384" && crv != "P-521" { + return nil, errors.New(fmt.Sprintf("Ecdh.Unwrap(): unknown or unsupported curve %v", crv)) + } + + if xBytes, err = base64url.Decode(x); err != nil { + return nil, err + } + if yBytes, err = base64url.Decode(y); err != nil { + return nil, err + } + + pubKey := ecc.NewPublic(xBytes, yBytes) + + if !privKey.Curve.IsOnCurve(pubKey.X, pubKey.Y) { + return nil, errors.New(fmt.Sprintf("Ephemeral public key received in header is invalid for reciever's private key.")) + } + + return alg.deriveKey(pubKey, privKey, cekSizeBits, header), nil + } + + return nil, errors.New("Ecdh.Unwrap(): expected key to be '*ecdsa.PrivateKey'") +} + +func (alg *Ecdh) deriveKey(pubKey *ecdsa.PublicKey, privKey *ecdsa.PrivateKey, keySizeBits int, header map[string]interface{}) []byte { + + var enc, apv, apu []byte + var err error + + enc = []byte(header[alg.idHeader()].(string)) + + if a, ok := header["apv"].(string); !ok { + if apv, err = base64url.Decode(a); err != nil { + apv = nil + } + } + + if a, ok := header["apu"].(string); !ok { + if apu, err = base64url.Decode(a); err != nil { + apu = nil + } + } + + z, _ := pubKey.Curve.ScalarMult(pubKey.X, pubKey.Y, privKey.D.Bytes()) + zBytes := padding.Align(z.Bytes(), privKey.Curve.Params().BitSize) + + return kdf.DeriveConcatKDF(keySizeBits, zBytes, prependDatalen(enc), prependDatalen(apu), prependDatalen(apv), arrays.UInt32ToBytes(uint32(keySizeBits)), nil, sha256.New()) +} + +func (alg *Ecdh) idHeader() string { + if alg.directAgreement { + return "enc" + } + + return "alg" +} + +func name(curve elliptic.Curve) string { + return fmt.Sprintf("P-%v", curve.Params().BitSize) +} + +func prependDatalen(bytes []byte) []byte { + return arrays.Concat(arrays.UInt32ToBytes(uint32(len(bytes))), bytes) +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/ecdh_aeskw.go b/vendor/github.com/dvsekhvalnov/jose2go/ecdh_aeskw.go new file mode 100644 index 0000000..bff3480 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/ecdh_aeskw.go @@ -0,0 +1,42 @@ +package jose + +func init() { + RegisterJwa(&EcdhAesKW{ keySizeBits: 128, aesKW: &AesKW{ keySizeBits: 128}, ecdh: &Ecdh{directAgreement:false}}) + RegisterJwa(&EcdhAesKW{ keySizeBits: 192, aesKW: &AesKW{ keySizeBits: 192}, ecdh: &Ecdh{directAgreement:false}}) + RegisterJwa(&EcdhAesKW{ keySizeBits: 256, aesKW: &AesKW{ keySizeBits: 256}, ecdh: &Ecdh{directAgreement:false}}) +} + +// Elliptic curve Diffie–Hellman with AES Key Wrap key management algorithm implementation +type EcdhAesKW struct{ + keySizeBits int + aesKW JwaAlgorithm + ecdh JwaAlgorithm +} + +func (alg *EcdhAesKW) Name() string { + switch alg.keySizeBits { + case 128: return ECDH_ES_A128KW + case 192: return ECDH_ES_A192KW + default: return ECDH_ES_A256KW + } +} + +func (alg *EcdhAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + var kek []byte + + if kek,_,err=alg.ecdh.WrapNewKey(alg.keySizeBits, key, header);err!=nil { + return nil,nil,err + } + + return alg.aesKW.WrapNewKey(cekSizeBits,kek,header) +} + +func (alg *EcdhAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + var kek []byte + + if kek,err=alg.ecdh.Unwrap(nil, key, alg.keySizeBits, header);err!=nil { + return nil,err + } + + return alg.aesKW.Unwrap(encryptedCek,kek,cekSizeBits,header) +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/ecdsa_using_sha.go b/vendor/github.com/dvsekhvalnov/jose2go/ecdsa_using_sha.go new file mode 100644 index 0000000..23ac6ee --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/ecdsa_using_sha.go @@ -0,0 +1,76 @@ +package jose + +import ( + "crypto/rand" + "math/big" + "crypto/ecdsa" + "errors" + "github.com/dvsekhvalnov/jose2go/arrays" + "github.com/dvsekhvalnov/jose2go/padding" + "fmt" +) + +func init() { + RegisterJws(&EcdsaUsingSha{keySizeBits: 256, hashSizeBits: 256}) + RegisterJws(&EcdsaUsingSha{keySizeBits: 384, hashSizeBits: 384}) + RegisterJws(&EcdsaUsingSha{keySizeBits: 521, hashSizeBits: 512}) +} + +// ECDSA signing algorithm implementation +type EcdsaUsingSha struct{ + keySizeBits int + hashSizeBits int +} + +func (alg *EcdsaUsingSha) Name() string { + switch alg.keySizeBits { + case 256: return ES256 + case 384: return ES384 + default: return ES512 + } +} + +func (alg *EcdsaUsingSha) Verify(securedInput, signature []byte, key interface{}) error { + + if pubKey,ok:=key.(*ecdsa.PublicKey);ok { + + if sizeBits:=pubKey.Curve.Params().BitSize;sizeBits!=alg.keySizeBits { + return errors.New(fmt.Sprintf("EcdsaUsingSha.Verify(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits,sizeBits)) + } + + r:=new(big.Int).SetBytes(signature[:len(signature)/2]) + s:=new(big.Int).SetBytes(signature[len(signature)/2:]) + + if ok:=ecdsa.Verify(pubKey, sha(alg.hashSizeBits, securedInput), r,s); ok { + return nil + } + + return errors.New("EcdsaUsingSha.Verify(): Signature is not valid.") + } + + return errors.New("EcdsaUsingSha.Verify(): expects key to be '*ecdsa.PublicKey'") +} + +func (alg *EcdsaUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { + + if privKey,ok := key.(*ecdsa.PrivateKey);ok { + + if sizeBits:=privKey.Curve.Params().BitSize;sizeBits!=alg.keySizeBits { + return nil,errors.New(fmt.Sprintf("EcdsaUsingSha.Sign(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits,sizeBits)) + } + + var r,s *big.Int + + if r,s,err = ecdsa.Sign(rand.Reader, privKey, sha(alg.hashSizeBits, securedInput));err==nil { + + rBytes:=padding.Align(r.Bytes(), alg.keySizeBits) + sBytes:=padding.Align(s.Bytes(), alg.keySizeBits) + + return arrays.Concat(rBytes,sBytes),nil + } + + return nil, err + } + + return nil,errors.New("EcdsaUsingSha.Sign(): expects key to be '*ecdsa.PrivateKey'") +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/hmac.go b/vendor/github.com/dvsekhvalnov/jose2go/hmac.go new file mode 100644 index 0000000..d3726b7 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/hmac.go @@ -0,0 +1,13 @@ +package jose + +import ( + "crypto/hmac" + "hash" +) + +func calculateHmac(keySizeBits int, securedInput []byte, key []byte) []byte { + hasher := hmac.New(func() hash.Hash { return hashAlg(keySizeBits)}, key) + hasher.Write(securedInput) + + return hasher.Sum(nil) +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/hmac_using_sha.go b/vendor/github.com/dvsekhvalnov/jose2go/hmac_using_sha.go new file mode 100644 index 0000000..e0b664f --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/hmac_using_sha.go @@ -0,0 +1,46 @@ +package jose + +import ( + "crypto/hmac" + "errors" +) + +func init() { + RegisterJws(&HmacUsingSha{keySizeBits: 256}) + RegisterJws(&HmacUsingSha{keySizeBits: 384}) + RegisterJws(&HmacUsingSha{keySizeBits: 512}) +} + +// HMAC with SHA signing algorithm implementation +type HmacUsingSha struct{ + keySizeBits int +} + +func (alg *HmacUsingSha) Name() string { + switch alg.keySizeBits { + case 256: return HS256 + case 384: return HS384 + default: return HS512 + } +} + +func (alg *HmacUsingSha) Verify(securedInput, signature []byte, key interface{}) error { + + actualSig,_ := alg.Sign(securedInput, key) + + if !hmac.Equal(signature, actualSig) { + return errors.New("HmacUsingSha.Verify(): Signature is invalid") + } + + return nil +} + +func (alg *HmacUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { + //TODO: assert min key size + + if pubKey,ok:=key.([]byte); ok { + return calculateHmac(alg.keySizeBits, securedInput, pubKey),nil + } + + return nil,errors.New("HmacUsingSha.Sign(): expects key to be '[]byte' array") +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/jose.go b/vendor/github.com/dvsekhvalnov/jose2go/jose.go new file mode 100644 index 0000000..3549a91 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/jose.go @@ -0,0 +1,485 @@ +// Package jose provides high level functions for producing (signing, encrypting and +// compressing) or consuming (decoding) Json Web Tokens using Java Object Signing and Encryption spec +package jose + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/dvsekhvalnov/jose2go/compact" +) + +const ( + NONE = "none" //plaintext (unprotected) without signature / encryption + + HS256 = "HS256" //HMAC using SHA-256 hash + HS384 = "HS384" //HMAC using SHA-384 hash + HS512 = "HS512" //HMAC using SHA-512 hash + RS256 = "RS256" //RSASSA-PKCS-v1_5 using SHA-256 hash + RS384 = "RS384" //RSASSA-PKCS-v1_5 using SHA-384 hash + RS512 = "RS512" //RSASSA-PKCS-v1_5 using SHA-512 hash + PS256 = "PS256" //RSASSA-PSS using SHA-256 hash + PS384 = "PS384" //RSASSA-PSS using SHA-384 hash + PS512 = "PS512" //RSASSA-PSS using SHA-512 hash + ES256 = "ES256" //ECDSA using P-256 curve and SHA-256 hash + ES384 = "ES384" //ECDSA using P-384 curve and SHA-384 hash + ES512 = "ES512" //ECDSA using P-521 curve and SHA-512 hash + + A128CBC_HS256 = "A128CBC-HS256" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 256 bit key + A192CBC_HS384 = "A192CBC-HS384" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 384 bit key + A256CBC_HS512 = "A256CBC-HS512" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 512 bit key + A128GCM = "A128GCM" //AES in GCM mode with 128 bit key + A192GCM = "A192GCM" //AES in GCM mode with 192 bit key + A256GCM = "A256GCM" //AES in GCM mode with 256 bit key + + DIR = "dir" //Direct use of pre-shared symmetric key + RSA1_5 = "RSA1_5" //RSAES with PKCS #1 v1.5 padding, RFC 3447 + RSA_OAEP = "RSA-OAEP" //RSAES using Optimal Assymetric Encryption Padding, RFC 3447 + RSA_OAEP_256 = "RSA-OAEP-256" //RSAES using Optimal Assymetric Encryption Padding with SHA-256, RFC 3447 + A128KW = "A128KW" //AES Key Wrap Algorithm using 128 bit keys, RFC 3394 + A192KW = "A192KW" //AES Key Wrap Algorithm using 192 bit keys, RFC 3394 + A256KW = "A256KW" //AES Key Wrap Algorithm using 256 bit keys, RFC 3394 + A128GCMKW = "A128GCMKW" //AES GCM Key Wrap Algorithm using 128 bit keys + A192GCMKW = "A192GCMKW" //AES GCM Key Wrap Algorithm using 192 bit keys + A256GCMKW = "A256GCMKW" //AES GCM Key Wrap Algorithm using 256 bit keys + PBES2_HS256_A128KW = "PBES2-HS256+A128KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 128 bit key + PBES2_HS384_A192KW = "PBES2-HS384+A192KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 192 bit key + PBES2_HS512_A256KW = "PBES2-HS512+A256KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 256 bit key + ECDH_ES = "ECDH-ES" //Elliptic Curve Diffie Hellman key agreement + ECDH_ES_A128KW = "ECDH-ES+A128KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 128 bit key + ECDH_ES_A192KW = "ECDH-ES+A192KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 192 bit key + ECDH_ES_A256KW = "ECDH-ES+A256KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 256 bit key + + DEF = "DEF" //DEFLATE compression, RFC 1951 +) + +var jwsHashers = map[string]JwsAlgorithm{} +var jweEncryptors = map[string]JweEncryption{} +var jwaAlgorithms = map[string]JwaAlgorithm{} +var jwcCompressors = map[string]JwcAlgorithm{} + +// RegisterJwe register new encryption algorithm +func RegisterJwe(alg JweEncryption) { + jweEncryptors[alg.Name()] = alg +} + +// RegisterJwa register new key management algorithm +func RegisterJwa(alg JwaAlgorithm) { + jwaAlgorithms[alg.Name()] = alg +} + +// RegisterJws register new signing algorithm +func RegisterJws(alg JwsAlgorithm) { + jwsHashers[alg.Name()] = alg +} + +// RegisterJwc register new compression algorithm +func RegisterJwc(alg JwcAlgorithm) { + jwcCompressors[alg.Name()] = alg +} + +// DeregisterJwa deregister existing key management algorithm +func DeregisterJwa(alg string) JwaAlgorithm { + jwa := jwaAlgorithms[alg] + + delete(jwaAlgorithms, alg) + + return jwa +} + +// DeregisterJws deregister existing signing algorithm +func DeregisterJws(alg string) JwsAlgorithm { + jws := jwsHashers[alg] + + delete(jwsHashers, alg) + + return jws +} + +// DeregisterJws deregister existing encryption algorithm +func DeregisterJwe(alg string) JweEncryption { + jwe := jweEncryptors[alg] + + delete(jweEncryptors, alg) + + return jwe +} + +// DeregisterJwc deregister existing compression algorithm +func DeregisterJwc(alg string) JwcAlgorithm { + jwc := jwcCompressors[alg] + + delete(jwcCompressors, alg) + + return jwc +} + +// JweEncryption is a contract for implementing encryption algorithm +type JweEncryption interface { + Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) + Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) + KeySizeBits() int + Name() string +} + +// JwaAlgorithm is a contract for implementing key management algorithm +type JwaAlgorithm interface { + WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) + Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) + Name() string +} + +// JwsAlgorithm is a contract for implementing signing algorithm +type JwsAlgorithm interface { + Verify(securedInput, signature []byte, key interface{}) error + Sign(securedInput []byte, key interface{}) (signature []byte, err error) + Name() string +} + +// JwcAlgorithm is a contract for implementing compression algorithm +type JwcAlgorithm interface { + Compress(plainText []byte) []byte + Decompress(compressedText []byte) []byte + Name() string +} + +func Zip(alg string) func(cfg *JoseConfig) { + return func(cfg *JoseConfig) { + cfg.CompressionAlg = alg + } +} + +func Header(name string, value interface{}) func(cfg *JoseConfig) { + return func(cfg *JoseConfig) { + cfg.Headers[name] = value + } +} + +func Headers(headers map[string]interface{}) func(cfg *JoseConfig) { + return func(cfg *JoseConfig) { + for k, v := range headers { + cfg.Headers[k] = v + } + } +} + +type JoseConfig struct { + CompressionAlg string + Headers map[string]interface{} +} + +// Sign produces signed JWT token given arbitrary string payload, signature algorithm to use (see constants for list of supported algs), signing key and extra options (see option functions) +// Signing key is of different type for different signing alg, see specific +// signing alg implementation documentation. +// +// It returns 3 parts signed JWT token as string and not nil error if something went wrong. +func Sign(payload string, signingAlg string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { + return SignBytes([]byte(payload), signingAlg, key, options...) +} + +// Sign produces signed JWT token given arbitrary binary payload, signature algorithm to use (see constants for list of supported algs), signing key and extra options (see option functions) +// Signing key is of different type for different signing alg, see specific +// signing alg implementation documentation. +// +// It returns 3 parts signed JWT token as string and not nil error if something went wrong. +func SignBytes(payload []byte, signingAlg string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { + if signer, ok := jwsHashers[signingAlg]; ok { + + cfg := &JoseConfig{CompressionAlg: "", Headers: make(map[string]interface{})} + + //apply extra options + for _, option := range options { + option(cfg) + } + + //make sure defaults and requires are managed by us + cfg.Headers["alg"] = signingAlg + + paloadBytes := payload + var header []byte + var signature []byte + + if header, err = json.Marshal(cfg.Headers); err == nil { + securedInput := []byte(compact.Serialize(header, paloadBytes)) + + if signature, err = signer.Sign(securedInput, key); err == nil { + return compact.Serialize(header, paloadBytes, signature), nil + } + } + + return "", err + } + + return "", errors.New(fmt.Sprintf("jwt.Sign(): unknown algorithm: '%v'", signingAlg)) +} + +// Encrypt produces encrypted JWT token given arbitrary string payload, key management and encryption algorithms to use (see constants for list of supported algs) and management key. +// Management key is of different type for different key management alg, see specific +// key management alg implementation documentation. +// +// It returns 5 parts encrypted JWT token as string and not nil error if something went wrong. +func Encrypt(payload string, alg string, enc string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { + return EncryptBytes([]byte(payload), alg, enc, key, options...) +} + +// Encrypt produces encrypted JWT token given arbitrary binary payload, key management and encryption algorithms to use (see constants for list of supported algs) and management key. +// Management key is of different type for different key management alg, see specific +// key management alg implementation documentation. +// +// It returns 5 parts encrypted JWT token as string and not nil error if something went wrong. +func EncryptBytes(payload []byte, alg string, enc string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { + + cfg := &JoseConfig{CompressionAlg: "", Headers: make(map[string]interface{})} + + //apply extra options + for _, option := range options { + option(cfg) + } + + //make sure required headers are managed by us + cfg.Headers["alg"] = alg + cfg.Headers["enc"] = enc + + byteContent := payload + + if cfg.CompressionAlg != "" { + if zipAlg, ok := jwcCompressors[cfg.CompressionAlg]; ok { + byteContent = zipAlg.Compress([]byte(payload)) + cfg.Headers["zip"] = cfg.CompressionAlg + } else { + return "", errors.New(fmt.Sprintf("jwt.Compress(): Unknown compression method '%v'", cfg.CompressionAlg)) + } + + } else { + delete(cfg.Headers, "zip") //we not allow to manage 'zip' header manually for encryption + } + + return encrypt(byteContent, cfg.Headers, key) +} + +// This method is DEPRICATED and subject to be removed in next version. +// Use Encrypt(..) with Zip option instead. +// +// Compress produces encrypted & comressed JWT token given arbitrary payload, key management , encryption and compression algorithms to use (see constants for list of supported algs) and management key. +// Management key is of different type for different key management alg, see specific +// key management alg implementation documentation. +// +// It returns 5 parts encrypted & compressed JWT token as string and not nil error if something went wrong. +func Compress(payload string, alg string, enc string, zip string, key interface{}) (token string, err error) { + + if zipAlg, ok := jwcCompressors[zip]; ok { + compressed := zipAlg.Compress([]byte(payload)) + + jwtHeader := map[string]interface{}{ + "enc": enc, + "alg": alg, + "zip": zip, + } + + return encrypt(compressed, jwtHeader, key) + } + + return "", errors.New(fmt.Sprintf("jwt.Compress(): Unknown compression method '%v'", zip)) +} + +// Decode verifies, decrypts and decompresses given JWT token using management key. +// Management key is of different type for different key management or signing algorithms, see specific alg implementation documentation. +// +// Returns decoded payload as a string, headers and not nil error if something went wrong. +func Decode(token string, key interface{}) (string, map[string]interface{}, error) { + + payload, headers, err := DecodeBytes(token, key) + + if err != nil { + return "", nil, err + } + + return string(payload), headers, nil +} + +// Decode verifies, decrypts and decompresses given JWT token using management key. +// Management key is of different type for different key management or signing algorithms, see specific alg implementation documentation. +// +// Returns decoded payload as a raw bytes, headers and not nil error if something went wrong. +func DecodeBytes(token string, key interface{}) ([]byte, map[string]interface{}, error) { + parts, err := compact.Parse(token) + + if err != nil { + return nil, nil, err + } + + if len(parts) == 3 { + return verify(parts, key) + } + + if len(parts) == 5 { + return decrypt(parts, key) + } + + return nil, nil, errors.New(fmt.Sprintf("jwt.DecodeBytes() expects token of 3 or 5 parts, but was given: %v parts", len(parts))) +} + +func encrypt(payload []byte, jwtHeader map[string]interface{}, key interface{}) (token string, err error) { + var ok bool + var keyMgmtAlg JwaAlgorithm + var encAlg JweEncryption + + alg := jwtHeader["alg"].(string) + enc := jwtHeader["enc"].(string) + + if keyMgmtAlg, ok = jwaAlgorithms[alg]; !ok { + return "", errors.New(fmt.Sprintf("jwt.encrypt(): Unknown key management algorithm '%v'", alg)) + } + + if encAlg, ok = jweEncryptors[enc]; !ok { + return "", errors.New(fmt.Sprintf("jwt.encrypt(): Unknown encryption algorithm '%v'", enc)) + } + + var cek, encryptedCek, header, iv, cipherText, authTag []byte + + if cek, encryptedCek, err = keyMgmtAlg.WrapNewKey(encAlg.KeySizeBits(), key, jwtHeader); err != nil { + return "", err + } + + if header, err = json.Marshal(jwtHeader); err != nil { + return "", err + } + + if iv, cipherText, authTag, err = encAlg.Encrypt([]byte(compact.Serialize(header)), payload, cek); err != nil { + return "", err + } + + return compact.Serialize(header, encryptedCek, iv, cipherText, authTag), nil +} + +func verify(parts [][]byte, key interface{}) (plainText []byte, headers map[string]interface{}, err error) { + + header, payload, signature := parts[0], parts[1], parts[2] + + secured := []byte(compact.Serialize(header, payload)) + + var jwtHeader map[string]interface{} + + if err = json.Unmarshal(header, &jwtHeader); err != nil { + return nil, nil, err + } + + if alg, ok := jwtHeader["alg"].(string); ok { + if verifier, ok := jwsHashers[alg]; ok { + if key, err = retrieveActualKey(jwtHeader, string(payload), key); err != nil { + return nil, nil, err + } + + if err = verifier.Verify(secured, signature, key); err == nil { + return payload, jwtHeader, nil + } + + return nil, nil, err + } + + return nil, nil, errors.New(fmt.Sprintf("jwt.Decode(): Unknown algorithm: '%v'", alg)) + } + + return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'alg' header is missing or of invalid type")) +} + +func decrypt(parts [][]byte, key interface{}) (plainText []byte, headers map[string]interface{}, err error) { + + header, encryptedCek, iv, cipherText, authTag := parts[0], parts[1], parts[2], parts[3], parts[4] + + var jwtHeader map[string]interface{} + + if e := json.Unmarshal(header, &jwtHeader); e != nil { + return nil, nil, e + } + + var keyMgmtAlg JwaAlgorithm + var encAlg JweEncryption + var zipAlg JwcAlgorithm + var cek, plainBytes []byte + var ok bool + var alg, enc string + + if alg, ok = jwtHeader["alg"].(string); !ok { + return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'alg' header is missing or of invalid type")) + } + + if enc, ok = jwtHeader["enc"].(string); !ok { + return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'enc' header is missing or of invalid type")) + } + + aad := []byte(compact.Serialize(header)) + + if keyMgmtAlg, ok = jwaAlgorithms[alg]; ok { + if encAlg, ok = jweEncryptors[enc]; ok { + + if key, err = retrieveActualKey(jwtHeader, string(cipherText), key); err != nil { + return nil, nil, err + } + + if cek, err = keyMgmtAlg.Unwrap(encryptedCek, key, encAlg.KeySizeBits(), jwtHeader); err == nil { + if plainBytes, err = encAlg.Decrypt(aad, cek, iv, cipherText, authTag); err == nil { + + if zip, compressed := jwtHeader["zip"].(string); compressed { + + if zipAlg, ok = jwcCompressors[zip]; !ok { + return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown compression algorithm '%v'", zip)) + } + + plainBytes = zipAlg.Decompress(plainBytes) + } + + return plainBytes, jwtHeader, nil + } + + return nil, nil, err + } + + return nil, nil, err + } + + return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown encryption algorithm '%v'", enc)) + } + + return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown key management algorithm '%v'", alg)) +} + +func retrieveActualKey(headers map[string]interface{}, payload string, key interface{}) (interface{}, error) { + if keyCallback, ok := key.(func(headers map[string]interface{}, payload string) interface{}); ok { + result := keyCallback(headers, payload) + + if err, ok := result.(error); ok { + return nil, err + } + + return result, nil + } + + return key, nil +} + +func Alg(key interface{}, jws string) func(headers map[string]interface{}, payload string) interface{} { + return func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + + if jws == alg { + return key + } + + return errors.New("Expected alg to be '" + jws + "' but got '" + alg + "'") + } +} + +func Enc(key interface{}, jwa string, jwe string) func(headers map[string]interface{}, payload string) interface{} { + return func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + enc := headers["enc"].(string) + + if jwa == alg && jwe == enc { + return key + } + + return errors.New("Expected alg to be '" + jwa + "' and enc to be '" + jwe + "' but got '" + alg + "' and '" + enc + "'") + } +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/kdf/nist_sp800_56a.go b/vendor/github.com/dvsekhvalnov/jose2go/kdf/nist_sp800_56a.go new file mode 100644 index 0000000..dd76790 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/kdf/nist_sp800_56a.go @@ -0,0 +1,43 @@ +package kdf + +import ( + "hash" + "math" + "github.com/dvsekhvalnov/jose2go/arrays" +) + +const ( + MaxInt = int(^uint(0)>>1); +) + +// DeriveConcatKDF implements NIST SP 800-56A Concatenation Key Derivation Function. Derives +// key material of keydatalen bits size given Z (sharedSecret), OtherInfo (AlgorithmID | +// PartyUInfo | PartyVInfo | SuppPubInfo | SuppPrivInfo) and hash function +func DeriveConcatKDF(keydatalen int, sharedSecret, algId, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo []byte, h hash.Hash) []byte { + + otherInfo := arrays.Concat(algId, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo) + + keyLenBytes := keydatalen >> 3 + + reps := int(math.Ceil(float64(keyLenBytes) / float64(h.Size()))) + + if reps > MaxInt { + panic("kdf.DeriveConcatKDF: too much iterations (more than 2^32-1).") + } + + dk:=make([]byte, 0, keyLenBytes) + + for counter := 1;counter <= reps;counter++ { + h.Reset() + + counterBytes:=arrays.UInt32ToBytes(uint32(counter)) + + h.Write(counterBytes) + h.Write(sharedSecret) + h.Write(otherInfo) + + dk = h.Sum(dk) + } + + return dk[:keyLenBytes] +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go b/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go new file mode 100644 index 0000000..aec58e7 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go @@ -0,0 +1,63 @@ +// package kdf contains implementations of various key derivation functions +package kdf + +import ( + "crypto/hmac" + "fmt" + "hash" + "math" + + "github.com/dvsekhvalnov/jose2go/arrays" +) + +// DerivePBKDF2 implements Password Based Key Derivation Function 2, RFC 2898. Derives key of keyBitLength size, given password, salt, iteration count and hash function +func DerivePBKDF2(password, salt []byte, iterationCount, keyBitLength int, h func() hash.Hash) []byte { + + prf := hmac.New(h, password) + hLen := prf.Size() + dkLen := keyBitLength >> 3 //size of derived key in bytes + + l := int(math.Ceil(float64(dkLen) / float64(hLen))) // l = CEIL (dkLen / hLen) + r := dkLen - (l-1)*hLen + + // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. + if dkLen > MaxInt { + panic(fmt.Sprintf("kdf.DerivePBKDF2: expects derived key size to be not more that (2^32-1) bits, but was requested %v bits.", keyBitLength)) + } + + dk := make([]byte, 0, dkLen) + + for i := 0; i < l; i++ { + + t := f(salt, iterationCount, i+1, prf) // T_l = F (P, S, c, l) + + if i == (l - 1) { + t = t[:r] + } // truncate last block to r bits + + dk = append(dk, t...) // DK = T_1 || T_2 || ... || T_l<0..r-1> + } + + return dk +} + +func f(salt []byte, iterationCount, blockIndex int, prf hash.Hash) []byte { + + prf.Reset() + prf.Write(salt) + prf.Write(arrays.UInt32ToBytes(uint32(blockIndex))) + + u := prf.Sum(nil) // U_1 = PRF (P, S || INT (i)) + + result := u + + for i := 2; i <= iterationCount; i++ { + prf.Reset() + prf.Write(u) + + u = prf.Sum(nil) // U_c = PRF (P, U_{c-1}) . + result = arrays.Xor(result, u) // U_1 \xor U_2 \xor ... \xor U_c + } + + return result +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_cert.pem b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_cert.pem new file mode 100644 index 0000000..384cb46 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICJjCCAc6gAwIBAgIJAOCtH/xv+cfpMAkGByqGSM49BAEwRTELMAkGA1UEBhMC +QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDAeFw0xNDA4MTIxMTU5MTVaFw0xODA1MDgxMTU5MTVaMEUxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASqGi1p +Eudyh+Nw//zeAGIYSQG/H/q/x6Xz2TIg3GN4hbD5BS1f4/vEitpui+TpmtJyggzo +x5D55N9kjq0X/hyyo4GnMIGkMB0GA1UdDgQWBBQvGSEecx9JdDECRIorVpeWy7oA +ujB1BgNVHSMEbjBsgBQvGSEecx9JdDECRIorVpeWy7oAuqFJpEcwRTELMAkGA1UE +BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp +ZGdpdHMgUHR5IEx0ZIIJAOCtH/xv+cfpMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0E +AQNHADBEAiAUQheZrGjbsy6PfpWGZEhTFzqvBVXtbqtz+6aTkOCrCQIgLfvw9C+0 +SDn/abV4NtgYOM0OLkoNRTCIzzguHxhhaJ4= +-----END CERTIFICATE----- diff --git a/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.key b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.key new file mode 100644 index 0000000..399b84b --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIA/32XgQpS/tKRhw8jSdr8ivBmMyD/vbG5KT3s3XPArcoAoGCCqGSM49 +AwEHoUQDQgAEqhotaRLncofjcP/83gBiGEkBvx/6v8el89kyINxjeIWw+QUtX+P7 +xIrabovk6ZrScoIM6MeQ+eTfZI6tF/4csg== +-----END EC PRIVATE KEY----- diff --git a/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.pem b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.pem new file mode 100644 index 0000000..5247be2 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_private.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgD/fZeBClL+0pGHDy +NJ2vyK8GYzIP+9sbkpPezdc8CtyhRANCAASqGi1pEudyh+Nw//zeAGIYSQG/H/q/ +x6Xz2TIg3GN4hbD5BS1f4/vEitpui+TpmtJyggzox5D55N9kjq0X/hyy +-----END PRIVATE KEY----- diff --git a/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_public.key b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_public.key new file mode 100644 index 0000000..bc2345d --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ec_public.key @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqhotaRLncofjcP/83gBiGEkBvx/6 +v8el89kyINxjeIWw+QUtX+P7xIrabovk6ZrScoIM6MeQ+eTfZI6tF/4csg== +-----END PUBLIC KEY----- diff --git a/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ecc.go b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ecc.go new file mode 100644 index 0000000..486d816 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/keys/ecc/ecc.go @@ -0,0 +1,92 @@ +//package ecc provides helpers for creating elliptic curve leys +package ecc + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/x509" + "encoding/pem" + "errors" + "math/big" +) + +// ReadPublic loads ecdsa.PublicKey from given PKCS1 X509 or PKIX blobs +func ReadPublic(raw []byte) (key *ecdsa.PublicKey, err error) { + var encoded *pem.Block + + if encoded, _ = pem.Decode(raw); encoded == nil { + return nil, errors.New("Ecc.ReadPublic(): Key must be PEM encoded PKCS1 X509 certificate or PKIX EC public key") + } + + var parsedKey interface{} + var cert *x509.Certificate + + if parsedKey, err = x509.ParsePKIXPublicKey(encoded.Bytes); err != nil { + if cert, err = x509.ParseCertificate(encoded.Bytes); err != nil { + return nil, err + } + + parsedKey = cert.PublicKey + } + + var ok bool + + if key, ok = parsedKey.(*ecdsa.PublicKey); !ok { + return nil, errors.New("Ecc.ReadPublic(): Key is not a valid *ecdsa.PublicKey") + } + + return key, nil +} + +// ReadPrivate loads ecdsa.PrivateKey from given PKCS1 or PKCS8 blobs +func ReadPrivate(raw []byte) (key *ecdsa.PrivateKey, err error) { + var encoded *pem.Block + + if encoded, _ = pem.Decode(raw); encoded == nil { + return nil, errors.New("Ecc.ReadPrivate(): Key must be PEM encoded PKCS1 or PKCS8 EC private key") + } + + var parsedKey interface{} + + if parsedKey, err = x509.ParseECPrivateKey(encoded.Bytes); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(encoded.Bytes); err != nil { + return nil, err + } + } + + var ok bool + + if key, ok = parsedKey.(*ecdsa.PrivateKey); !ok { + return nil, errors.New("Ecc.ReadPrivate(): Key is not valid *ecdsa.PrivateKey") + } + + return key, nil +} + +// NewPublic constructs ecdsa.PublicKey from given (X,Y) +func NewPublic(x, y []byte) *ecdsa.PublicKey { + return &ecdsa.PublicKey{Curve: curve(len(x)), + X: new(big.Int).SetBytes(x), + Y: new(big.Int).SetBytes(y)} +} + +// NewPrivate constructs ecdsa.PrivateKey from given (X,Y) and D +func NewPrivate(x, y, d []byte) *ecdsa.PrivateKey { + return &ecdsa.PrivateKey{D: new(big.Int).SetBytes(d), + PublicKey: ecdsa.PublicKey{Curve: curve(len(x)), + X: new(big.Int).SetBytes(x), + Y: new(big.Int).SetBytes(y)}} +} + +func curve(size int) elliptic.Curve { + switch size { + case 31, 32: + return elliptic.P256() + case 48: + return elliptic.P384() + case 65, 66: + return elliptic.P521() //adjust for P-521 curve, which can be 65 or 66 bytes + default: + return nil //unsupported curve + } +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/padding/align.go b/vendor/github.com/dvsekhvalnov/jose2go/padding/align.go new file mode 100644 index 0000000..0ef601f --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/padding/align.go @@ -0,0 +1,23 @@ +// package padding provides various padding algorithms +package padding + +import ( + "bytes" +) + +// Align left pads given byte array with zeros till it have at least bitSize length. +func Align(data []byte, bitSize int) []byte { + + actual:=len(data) + required:=bitSize >> 3 + + if (bitSize % 8) > 0 { + required++ //extra byte if needed + } + + if (actual >= required) { + return data + } + + return append(bytes.Repeat([]byte{0}, required-actual), data...) +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/padding/pkcs7.go b/vendor/github.com/dvsekhvalnov/jose2go/padding/pkcs7.go new file mode 100644 index 0000000..2f64e7e --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/padding/pkcs7.go @@ -0,0 +1,38 @@ +package padding + +import ( + "bytes" +) + +// AddPkcs7 pads given byte array using pkcs7 padding schema till it has blockSize length in bytes +func AddPkcs7(data []byte, blockSize int) []byte { + + var paddingCount int + + if paddingCount = blockSize - (len(data) % blockSize);paddingCount == 0 { + paddingCount=blockSize + } + + return append(data, bytes.Repeat([]byte{byte(paddingCount)}, paddingCount)...) +} + +// RemovePkcs7 removes pkcs7 padding from previously padded byte array +func RemovePkcs7(padded []byte, blockSize int) []byte { + + dataLen:=len(padded) + paddingCount:=int(padded[dataLen-1]) + + if(paddingCount > blockSize || paddingCount <= 0) { + return padded //data is not padded (or not padded correctly), return as is + } + + padding := padded[dataLen-paddingCount : dataLen-1] + + for _, b := range padding { + if int(b) != paddingCount { + return padded //data is not padded (or not padded correcly), return as is + } + } + + return padded[:len(padded)-paddingCount] //return data - padding +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go b/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go new file mode 100644 index 0000000..2915ae6 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go @@ -0,0 +1,142 @@ +package jose + +import ( + "crypto/sha256" + "crypto/sha512" + "errors" + "fmt" + "hash" + + "github.com/dvsekhvalnov/jose2go/arrays" + "github.com/dvsekhvalnov/jose2go/base64url" + "github.com/dvsekhvalnov/jose2go/kdf" +) + +func init() { + RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 0)) + RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 0)) + RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 0)) +} + +// PBSE2 with HMAC key management algorithm implementation +type Pbse2HmacAesKW struct { + keySizeBits int + aesKW JwaAlgorithm + maxIterations int64 + minIterations int64 +} + +func NewPbse2HmacAesKWAlg(keySize int, maxIters int64, minIters int64) JwaAlgorithm { + switch keySize { + case 128: + return &Pbse2HmacAesKW{keySizeBits: 128, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 128}} + case 192: + return &Pbse2HmacAesKW{keySizeBits: 192, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 192}} + default: + return &Pbse2HmacAesKW{keySizeBits: 256, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 256}} + } +} + +func (alg *Pbse2HmacAesKW) Name() string { + switch alg.keySizeBits { + case 128: + return PBES2_HS256_A128KW + case 192: + return PBES2_HS384_A192KW + default: + return PBES2_HS512_A256KW + } +} + +func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if passphrase, ok := key.(string); ok { + + algId := []byte(header["alg"].(string)) + + iterationCount := 8192 + var saltInput []byte + + if saltInput, err = arrays.Random(12); err != nil { + return nil, nil, err + } + + // use user provided iteration counts if any + if p2c, ok := header["p2c"].(int); ok { + iterationCount = p2c + } + + if int64(iterationCount) > alg.maxIterations { + return nil, nil, errors.New( + fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, iterationCount)) + } + + if int64(iterationCount) < alg.minIterations { + return nil, nil, errors.New( + fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, iterationCount)) + } + + header["p2c"] = iterationCount + header["p2s"] = base64url.Encode(saltInput) + + salt := arrays.Concat(algId, []byte{0}, saltInput) + + kek := kdf.DerivePBKDF2([]byte(passphrase), salt, iterationCount, alg.keySizeBits, alg.prf) + return alg.aesKW.WrapNewKey(cekSizeBits, kek, header) + } + + return nil, nil, errors.New("Pbse2HmacAesKW.WrapNewKey(): expected key to be 'string' array") +} + +func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + + if passphrase, ok := key.(string); ok { + + var p2s string + var p2c float64 + + if p2c, ok = header["p2c"].(float64); !ok { + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2c' param in JWT header, but was not found.") + } + + if int64(p2c) > alg.maxIterations { + return nil, errors.New( + fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, p2c)) + } + + if int64(p2c) < alg.minIterations { + return nil, errors.New( + fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, p2c)) + } + + if p2s, ok = header["p2s"].(string); !ok { + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found") + } + + var saltInput []byte + + algId := []byte(header["alg"].(string)) + + if saltInput, err = base64url.Decode(p2s); err != nil { + return nil, err + } + + salt := arrays.Concat(algId, []byte{0}, saltInput) + + kek := kdf.DerivePBKDF2([]byte(passphrase), salt, int(p2c), alg.keySizeBits, alg.prf) + + return alg.aesKW.Unwrap(encryptedCek, kek, cekSizeBits, header) + } + + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected key to be 'string' array") +} + +func (alg *Pbse2HmacAesKW) prf() hash.Hash { + switch alg.keySizeBits { + case 128: + return sha256.New() + case 192: + return sha512.New384() + default: + return sha512.New() + } +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/plaintext.go b/vendor/github.com/dvsekhvalnov/jose2go/plaintext.go new file mode 100644 index 0000000..761ce5a --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/plaintext.go @@ -0,0 +1,38 @@ +package jose + +import ( + "errors" +) + +// Plaintext (no signing) signing algorithm implementation +type Plaintext struct{} + +func init() { + RegisterJws(new(Plaintext)) +} + +func (alg *Plaintext) Name() string { + return NONE +} + +func (alg *Plaintext) Verify(securedInput []byte, signature []byte, key interface{}) error { + + if key != nil { + return errors.New("Plaintext.Verify() expects key to be nil") + } + + if len(signature) != 0 { + return errors.New("Plaintext.Verify() expects signature to be empty.") + } + + return nil +} + +func (alg *Plaintext) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { + + if key != nil { + return nil, errors.New("Plaintext.Verify() expects key to be nil") + } + + return []byte{}, nil +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/rsa_oaep.go b/vendor/github.com/dvsekhvalnov/jose2go/rsa_oaep.go new file mode 100644 index 0000000..b0d1b52 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/rsa_oaep.go @@ -0,0 +1,57 @@ +package jose + +import ( + "errors" + "crypto/rsa" + "crypto/rand" + "hash" + "crypto/sha1" + "crypto/sha256" + "github.com/dvsekhvalnov/jose2go/arrays" +) + +// RS-AES using OAEP key management algorithm implementation +func init() { + RegisterJwa(&RsaOaep {shaSizeBits:1}) + RegisterJwa(&RsaOaep {shaSizeBits:256}) +} + +type RsaOaep struct{ + shaSizeBits int + // func shaF() hash.Hash +} + +func (alg *RsaOaep) Name() string { + switch alg.shaSizeBits { + case 1: return RSA_OAEP + default: return RSA_OAEP_256 + } +} + +func (alg *RsaOaep) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if pubKey,ok:=key.(*rsa.PublicKey);ok { + if cek,err = arrays.Random(cekSizeBits>>3);err==nil { + encryptedCek,err=rsa.EncryptOAEP(alg.sha(),rand.Reader,pubKey,cek,nil) + return + } + + return nil,nil,err + } + + return nil,nil,errors.New("RsaOaep.WrapNewKey(): expected key to be '*rsa.PublicKey'") +} + +func (alg *RsaOaep) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + if privKey,ok:=key.(*rsa.PrivateKey);ok { + return rsa.DecryptOAEP(alg.sha(), rand.Reader, privKey, encryptedCek, nil) + } + + return nil,errors.New("RsaOaep.Unwrap(): expected key to be '*rsa.PrivateKey'") +} + +func (alg *RsaOaep) sha() hash.Hash { + switch alg.shaSizeBits { + case 1: return sha1.New() + default: return sha256.New() + } +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/rsa_pkcs1v15.go b/vendor/github.com/dvsekhvalnov/jose2go/rsa_pkcs1v15.go new file mode 100644 index 0000000..10dac06 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/rsa_pkcs1v15.go @@ -0,0 +1,41 @@ +package jose + +import ( + "errors" + "crypto/rsa" + "crypto/rand" + "github.com/dvsekhvalnov/jose2go/arrays" +) + +func init() { + RegisterJwa(new(RsaPkcs1v15)) +} + +// RS-AES using PKCS #1 v1.5 padding key management algorithm implementation +type RsaPkcs1v15 struct{ +} + +func (alg *RsaPkcs1v15) Name() string { + return RSA1_5 +} + +func (alg *RsaPkcs1v15) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if pubKey,ok:=key.(*rsa.PublicKey);ok { + if cek,err = arrays.Random(cekSizeBits>>3);err==nil { + encryptedCek,err=rsa.EncryptPKCS1v15(rand.Reader,pubKey,cek) + return + } + + return nil,nil,err + } + + return nil,nil,errors.New("RsaPkcs1v15.WrapNewKey(): expected key to be '*rsa.PublicKey'") +} + +func (alg *RsaPkcs1v15) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + if privKey,ok:=key.(*rsa.PrivateKey);ok { + return rsa.DecryptPKCS1v15(rand.Reader,privKey,encryptedCek) + } + + return nil,errors.New("RsaPkcs1v15.Unwrap(): expected key to be '*rsa.PrivateKey'") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/rsa_using_sha.go b/vendor/github.com/dvsekhvalnov/jose2go/rsa_using_sha.go new file mode 100644 index 0000000..647a45a --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/rsa_using_sha.go @@ -0,0 +1,50 @@ +package jose + +import ( + "crypto/rand" + "crypto/rsa" + "errors" +) + +func init() { + RegisterJws(&RsaUsingSha{keySizeBits: 256}) + RegisterJws(&RsaUsingSha{keySizeBits: 384}) + RegisterJws(&RsaUsingSha{keySizeBits: 512}) +} + +// RSA using SHA signature algorithm implementation +type RsaUsingSha struct{ + keySizeBits int +} + +func (alg *RsaUsingSha) Name() string { + switch alg.keySizeBits { + case 256: return RS256 + case 384: return RS384 + default: return RS512 + } +} + +func (alg *RsaUsingSha) Verify(securedInput, signature []byte, key interface{}) error { + + if pubKey,ok:=key.(*rsa.PublicKey);ok { + return rsa.VerifyPKCS1v15(pubKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), signature) + } + + return errors.New("RsaUsingSha.Verify(): expects key to be '*rsa.PublicKey'") +} + +func (alg *RsaUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { + + if privKey,ok:=key.(*rsa.PrivateKey);ok { + return rsa.SignPKCS1v15(rand.Reader, privKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput)) + } + + return nil,errors.New("RsaUsingSha.Sign(): expects key to be '*rsa.PrivateKey'") +} + +func sha(keySizeBits int, input []byte) (hash []byte) { + hasher := hashAlg(keySizeBits) + hasher.Write(input) + return hasher.Sum(nil) +}
\ No newline at end of file diff --git a/vendor/github.com/dvsekhvalnov/jose2go/rsapss_using_sha.go b/vendor/github.com/dvsekhvalnov/jose2go/rsapss_using_sha.go new file mode 100644 index 0000000..fc111db --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/rsapss_using_sha.go @@ -0,0 +1,43 @@ +package jose + +import ( + "crypto/rand" + "crypto/rsa" + "errors" +) + +func init() { + RegisterJws(&RsaPssUsingSha{keySizeBits: 256, saltSizeBytes: 32}) + RegisterJws(&RsaPssUsingSha{keySizeBits: 384, saltSizeBytes: 48}) + RegisterJws(&RsaPssUsingSha{keySizeBits: 512, saltSizeBytes: 64}) +} + +// RSA with PSS using SHA signing algorithm implementation +type RsaPssUsingSha struct{ + keySizeBits int + saltSizeBytes int +} + +func (alg *RsaPssUsingSha) Name() string { + switch alg.keySizeBits { + case 256: return PS256 + case 384: return PS384 + default: return PS512 + } +} + +func (alg *RsaPssUsingSha) Verify(securedInput, signature []byte, key interface{}) error { + if pubKey,ok:=key.(*rsa.PublicKey);ok { + return rsa.VerifyPSS(pubKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), signature, &rsa.PSSOptions{SaltLength:alg.saltSizeBytes}) + } + + return errors.New("RsaPssUsingSha.Verify(): expects key to be '*rsa.PublicKey'") +} + +func (alg *RsaPssUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { + if privKey,ok:=key.(*rsa.PrivateKey);ok { + return rsa.SignPSS(rand.Reader, privKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), &rsa.PSSOptions{SaltLength:alg.saltSizeBytes}) + } + + return nil,errors.New("RsaPssUsingSha.Sign(): expects key to be '*rsa.PrivateKey'") +} diff --git a/vendor/github.com/dvsekhvalnov/jose2go/sha.go b/vendor/github.com/dvsekhvalnov/jose2go/sha.go new file mode 100644 index 0000000..13b7b17 --- /dev/null +++ b/vendor/github.com/dvsekhvalnov/jose2go/sha.go @@ -0,0 +1,24 @@ +package jose + +import ( + "hash" + "crypto" + "crypto/sha256" + "crypto/sha512" +) + +func hashFunc(keySizeBits int) crypto.Hash { + switch keySizeBits { + case 256: return crypto.SHA256 + case 384: return crypto.SHA384 + default: return crypto.SHA512 + } +} + +func hashAlg(keySizeBits int) hash.Hash { + switch keySizeBits { + case 256: return sha256.New() + case 384: return sha512.New384() + default: return sha512.New() + } +}
\ No newline at end of file |
