├── keys ├── ecc │ ├── ec_public.key │ ├── ec_private.key │ ├── ec_private.pem │ ├── ec_cert.pem │ ├── ecc.go │ └── ecc_test.go └── rsa │ ├── pub.key │ ├── pub.pem │ ├── priv.pem │ ├── priv.key │ ├── rsa.go │ └── rsa_test.go ├── go.mod ├── hmac.go ├── .gitignore ├── sha.go ├── padding ├── align.go ├── align_test.go ├── pkcs7.go └── pkcs7_test.go ├── go.sum ├── .github └── workflows │ └── go.yml ├── plaintext.go ├── compact ├── compact.go └── compact_test.go ├── direct.go ├── base64url ├── base64url_test.go └── base64url.go ├── LICENSE ├── aes ├── ecb_test.go ├── ecb.go ├── key_wrap.go └── key_wrap_test.go ├── hmac_using_sha.go ├── kdf ├── nist_sp800_56a.go ├── pbkdf2.go ├── nist_sp800_56a_test.go └── pbkdf2_test.go ├── rsa_pkcs1v15.go ├── rsa_using_sha.go ├── rsapss_using_sha.go ├── ecdh_aeskw.go ├── deflate.go ├── aeskw.go ├── rsa_oaep.go ├── arrays ├── arrays_test.go └── arrays.go ├── ecdsa_using_sha.go ├── aes_gcm.go ├── aes_cbc_hmac.go ├── aes_gcm_kw.go ├── pbse2_hmac_aeskw.go ├── ecdh.go ├── sec_test └── security_vulnerabilities_test.go ├── jose.go └── README.md /keys/ecc/ec_public.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqhotaRLncofjcP/83gBiGEkBvx/6 3 | v8el89kyINxjeIWw+QUtX+P7xIrabovk6ZrScoIM6MeQ+eTfZI6tF/4csg== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dvsekhvalnov/jose2go 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 7 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b 8 | ) 9 | -------------------------------------------------------------------------------- /keys/ecc/ec_private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIA/32XgQpS/tKRhw8jSdr8ivBmMyD/vbG5KT3s3XPArcoAoGCCqGSM49 3 | AwEHoUQDQgAEqhotaRLncofjcP/83gBiGEkBvx/6v8el89kyINxjeIWw+QUtX+P7 4 | xIrabovk6ZrScoIM6MeQ+eTfZI6tF/4csg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /keys/ecc/ec_private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgD/fZeBClL+0pGHDy 3 | NJ2vyK8GYzIP+9sbkpPezdc8CtyhRANCAASqGi1pEudyh+Nw//zeAGIYSQG/H/q/ 4 | x6Xz2TIg3GN4hbD5BS1f4/vEitpui+TpmtJyggzox5D55N9kjq0X/hyy 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /keys/rsa/pub.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8fexkhwhpHr71ErFzLHDXftPD 3 | IzvAW5K885Q7qJCc1b0EKa0hBWAjtaI6dVIXgCJnkLzHtcAFqxjX2xcN+I0AkpL/ 4 | JYli4eWh6hz61PA04CrC/l4xxmjMLIPMuvKRJma6clfzePclypYdwtTy5xSa05ma 5 | tDxy6jlyDTbzFhb+4wIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /hmac.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/hmac" 5 | "hash" 6 | ) 7 | 8 | func calculateHmac(keySizeBits int, securedInput []byte, key []byte) []byte { 9 | hasher := hmac.New(func() hash.Hash { return hashAlg(keySizeBits)}, key) 10 | hasher.Write(securedInput) 11 | 12 | return hasher.Sum(nil) 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | -------------------------------------------------------------------------------- /sha.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "hash" 5 | "crypto" 6 | "crypto/sha256" 7 | "crypto/sha512" 8 | ) 9 | 10 | func hashFunc(keySizeBits int) crypto.Hash { 11 | switch keySizeBits { 12 | case 256: return crypto.SHA256 13 | case 384: return crypto.SHA384 14 | default: return crypto.SHA512 15 | } 16 | } 17 | 18 | func hashAlg(keySizeBits int) hash.Hash { 19 | switch keySizeBits { 20 | case 256: return sha256.New() 21 | case 384: return sha512.New384() 22 | default: return sha512.New() 23 | } 24 | } -------------------------------------------------------------------------------- /padding/align.go: -------------------------------------------------------------------------------- 1 | // package padding provides various padding algorithms 2 | package padding 3 | 4 | import ( 5 | "bytes" 6 | ) 7 | 8 | // Align left pads given byte array with zeros till it have at least bitSize length. 9 | func Align(data []byte, bitSize int) []byte { 10 | 11 | actual:=len(data) 12 | required:=bitSize >> 3 13 | 14 | if (bitSize % 8) > 0 { 15 | required++ //extra byte if needed 16 | } 17 | 18 | if (actual >= required) { 19 | return data 20 | } 21 | 22 | return append(bytes.Repeat([]byte{0}, required-actual), data...) 23 | } -------------------------------------------------------------------------------- /keys/rsa/pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBjTCB9wIEchk3tjANBgkqhkiG9w0BAQUFADAOMQwwCgYDVQQDEwNqd3QwHhcN 3 | MTQwMTA4MTM0NDUxWhcNMTUwMjA3MjAwMDAwWjAOMQwwCgYDVQQDEwNqd3QwgZ8w 4 | DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALx97GSHCGkevvUSsXMscNd+08MjO8Bb 5 | krzzlDuokJzVvQQprSEFYCO1ojp1UheAImeQvMe1wAWrGNfbFw34jQCSkv8liWLh 6 | 5aHqHPrU8DTgKsL+XjHGaMwsg8y68pEmZrpyV/N49yXKlh3C1PLnFJrTmZq0PHLq 7 | OXINNvMWFv7jAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAJcAJ4zK0ZZLUHRdxhQdj 8 | e0Xv7fPYosE7gV0apbPJXnuhU1XH4XKFQQNYWyxjwfFSjghAyyZqlfcWgl0STOXx 9 | JnFfEbSQL1EB5xKj9e1taMd+84mFXkVNvhN3wphe3EbPr9M99BRLic/MSGAqnJRS 10 | OPIdhqg16dZmPJ4kCG8lLmc= 11 | -----END CERTIFICATE----- 12 | 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 2 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 3 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 4 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 5 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 6 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 7 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 8 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Build and test jose2go project 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build-and-test: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [windows-latest, ubuntu-latest, macos-latest] 17 | version: ['1.20', '1.21', '1.22'] 18 | steps: 19 | - name: Checkout library sources 20 | uses: actions/checkout@v3 21 | 22 | - name: Set up Go 23 | uses: actions/setup-go@v4 24 | with: 25 | go-version: ${{ matrix.version }} 26 | 27 | - name: Build 28 | run: go build -v ./... 29 | 30 | - name: Test 31 | run: go test ./... 32 | 33 | -------------------------------------------------------------------------------- /keys/ecc/ec_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICJjCCAc6gAwIBAgIJAOCtH/xv+cfpMAkGByqGSM49BAEwRTELMAkGA1UEBhMC 3 | QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp 4 | dHMgUHR5IEx0ZDAeFw0xNDA4MTIxMTU5MTVaFw0xODA1MDgxMTU5MTVaMEUxCzAJ 5 | BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l 6 | dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASqGi1p 7 | Eudyh+Nw//zeAGIYSQG/H/q/x6Xz2TIg3GN4hbD5BS1f4/vEitpui+TpmtJyggzo 8 | x5D55N9kjq0X/hyyo4GnMIGkMB0GA1UdDgQWBBQvGSEecx9JdDECRIorVpeWy7oA 9 | ujB1BgNVHSMEbjBsgBQvGSEecx9JdDECRIorVpeWy7oAuqFJpEcwRTELMAkGA1UE 10 | BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp 11 | ZGdpdHMgUHR5IEx0ZIIJAOCtH/xv+cfpMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0E 12 | AQNHADBEAiAUQheZrGjbsy6PfpWGZEhTFzqvBVXtbqtz+6aTkOCrCQIgLfvw9C+0 13 | SDn/abV4NtgYOM0OLkoNRTCIzzguHxhhaJ4= 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /plaintext.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Plaintext (no signing) signing algorithm implementation 8 | type Plaintext struct{} 9 | 10 | func init() { 11 | RegisterJws(new(Plaintext)) 12 | } 13 | 14 | func (alg *Plaintext) Name() string { 15 | return NONE 16 | } 17 | 18 | func (alg *Plaintext) Verify(securedInput []byte, signature []byte, key interface{}) error { 19 | 20 | if key != nil { 21 | return errors.New("Plaintext.Verify() expects key to be nil") 22 | } 23 | 24 | if len(signature) != 0 { 25 | return errors.New("Plaintext.Verify() expects signature to be empty.") 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func (alg *Plaintext) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { 32 | 33 | if key != nil { 34 | return nil, errors.New("Plaintext.Verify() expects key to be nil") 35 | } 36 | 37 | return []byte{}, nil 38 | } 39 | -------------------------------------------------------------------------------- /keys/rsa/priv.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQC8fexkhwhpHr71ErFzLHDXftPDIzvAW5K885Q7qJCc1b0EKa0h 3 | BWAjtaI6dVIXgCJnkLzHtcAFqxjX2xcN+I0AkpL/JYli4eWh6hz61PA04CrC/l4x 4 | xmjMLIPMuvKRJma6clfzePclypYdwtTy5xSa05matDxy6jlyDTbzFhb+4wIDAQAB 5 | AoGBALGHNEc2OwCu2ZF+yqy8NYdxR0dp/tPcK6dVDs7lo1ZjpMOv8QTByFu5rZK7 6 | ftbtcvnyAythjfqIvDxJeDxLVZiPC42hMS7uX7Kf2fgWx33LOC+UqPaXRmZ+Q4X4 7 | etOi1kWdKqko3bV4raTTK48e9+vibSev5B5QnZmA8k4bZY2xAkEA6AwwM58oumJd 8 | k/rWqItlXAj+8BQCGH9zLup6ohAYXKdZVTrlo8kfpgVPPqOoQ7ONKFQDnaQnarQy 9 | d6aVgASimQJBAM/yyKvFMKvHqYTcJ1jkroZI1M/jtNb++Bh8kurXO1hnJBUeyUtX 10 | LoIV5UUau4Q9c9XYU2KtFtoOGK7o3xhU1tsCQQCMLUC5Dn6mCWIKUM93xa2XaX/I 11 | O+4AKo3EW7V1TU/KOPYIPKoYxRivTCnQ50BwlXjCUe5VPqLpHK4YBbPuP96RAkAX 12 | L1+WXz8E+Bb7fDxa2exnhnlL0HyaOAVbXZBX5LV2LtHEFRGmd8r0sBJG+SsNHHjs 13 | z9jEOL5Cg63W2pn9MYBfAkBYs9j/IVav0N3xSglmpmdaJcdPM6OMeMtpGCjtu+Rk 14 | mWm4YlaLpVYVLUYIvQXD31vZGpKqbMBHuP/xHkm5d/AZ 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /compact/compact.go: -------------------------------------------------------------------------------- 1 | // package compact provides function to work with json compact serialization format 2 | package compact 3 | 4 | import ( 5 | "github.com/dvsekhvalnov/jose2go/base64url" 6 | "strings" 7 | ) 8 | 9 | // Parse splitting & decoding compact serialized json web token, returns slice of byte arrays, each representing part of token 10 | func Parse(token string) (result [][]byte, e error) { 11 | parts := strings.Split(token, ".") 12 | 13 | result = make([][]byte, len(parts)) 14 | 15 | for i, part := range parts { 16 | if result[i], e = base64url.Decode(part); e != nil { 17 | return nil, e 18 | } 19 | } 20 | 21 | return result, nil 22 | } 23 | 24 | // Serialize converts given parts into compact serialization format 25 | func Serialize(parts ...[]byte) string { 26 | result := make([]string, len(parts)) 27 | 28 | for i, part := range parts { 29 | result[i] = base64url.Encode(part) 30 | } 31 | 32 | return strings.Join(result, ".") 33 | } 34 | -------------------------------------------------------------------------------- /padding/align_test.go: -------------------------------------------------------------------------------- 1 | package padding 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | ) 6 | 7 | func (s *TestSuite) TestAlignOneByte(c *C) { 8 | //given 9 | data:=[]byte{1,2,3} 10 | 11 | //when 12 | test:=Align(data,27) 13 | 14 | //then 15 | c.Assert(test, DeepEquals, []byte{0,1,2,3}) 16 | 17 | } 18 | 19 | func (s *TestSuite) TestAlignMultiByte(c *C) { 20 | //given 21 | data:=[]byte{1,2,3} 22 | 23 | //when 24 | test:=Align(data,40) 25 | 26 | //then 27 | c.Assert(test, DeepEquals, []byte{0,0,1,2,3}) 28 | 29 | } 30 | 31 | func (s *TestSuite) TestAlignMultiBytePartial(c *C) { 32 | //given 33 | data:=[]byte{1,2,3} 34 | 35 | //when 36 | test:=Align(data,43) 37 | 38 | //then 39 | c.Assert(test, DeepEquals, []byte{0,0,0,1,2,3}) 40 | 41 | } 42 | 43 | func (s *TestSuite) TestAlignedArray(c *C) { 44 | //given 45 | data:=[]byte{1,2,3} 46 | 47 | //when 48 | test:=Align(data,24) 49 | 50 | //then 51 | c.Assert(test, DeepEquals, []byte{1,2,3}) 52 | } -------------------------------------------------------------------------------- /keys/rsa/priv.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALx97GSHCGkevvUS 3 | sXMscNd+08MjO8BbkrzzlDuokJzVvQQprSEFYCO1ojp1UheAImeQvMe1wAWrGNfb 4 | Fw34jQCSkv8liWLh5aHqHPrU8DTgKsL+XjHGaMwsg8y68pEmZrpyV/N49yXKlh3C 5 | 1PLnFJrTmZq0PHLqOXINNvMWFv7jAgMBAAECgYEAsYc0RzY7AK7ZkX7KrLw1h3FH 6 | R2n+09wrp1UOzuWjVmOkw6/xBMHIW7mtkrt+1u1y+fIDK2GN+oi8PEl4PEtVmI8L 7 | jaExLu5fsp/Z+BbHfcs4L5So9pdGZn5Dhfh606LWRZ0qqSjdtXitpNMrjx736+Jt 8 | J6/kHlCdmYDyThtljbECQQDoDDAznyi6Yl2T+taoi2VcCP7wFAIYf3Mu6nqiEBhc 9 | p1lVOuWjyR+mBU8+o6hDs40oVAOdpCdqtDJ3ppWABKKZAkEAz/LIq8Uwq8ephNwn 10 | WOSuhkjUz+O01v74GHyS6tc7WGckFR7JS1cughXlRRq7hD1z1dhTYq0W2g4Yrujf 11 | GFTW2wJBAIwtQLkOfqYJYgpQz3fFrZdpf8g77gAqjcRbtXVNT8o49gg8qhjFGK9M 12 | KdDnQHCVeMJR7lU+oukcrhgFs+4/3pECQBcvX5ZfPwT4Fvt8PFrZ7GeGeUvQfJo4 13 | BVtdkFfktXYu0cQVEaZ3yvSwEkb5Kw0ceOzP2MQ4vkKDrdbamf0xgF8CQFiz2P8h 14 | Vq/Q3fFKCWamZ1olx08zo4x4y2kYKO275GSZabhiVoulVhUtRgi9BcPfW9kakqps 15 | wEe4//EeSbl38Bk= 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /direct.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | func init() { 8 | RegisterJwa(new(Direct)) 9 | } 10 | 11 | // Direct (pre-shared) key management algorithm implementation 12 | type Direct struct{ 13 | } 14 | 15 | func (alg *Direct) Name() string { 16 | return DIR 17 | } 18 | 19 | func (alg *Direct) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 20 | 21 | if cek,ok:=key.([]byte); ok { 22 | return cek,[]byte{},nil 23 | } 24 | 25 | return nil,nil,errors.New("Direct.WrapNewKey(): expected key to be '[]byte' array") 26 | } 27 | 28 | func (alg *Direct) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 29 | 30 | if(len(encryptedCek)!=0) { 31 | return nil, errors.New("Direct.Unwrap(): expected empty encrypted CEK") 32 | } 33 | 34 | if cek,ok:=key.([]byte); ok { 35 | return cek,nil 36 | } 37 | 38 | return nil,errors.New("Direct.Unwrap(): expected key to be '[]byte' array") 39 | } 40 | -------------------------------------------------------------------------------- /base64url/base64url_test.go: -------------------------------------------------------------------------------- 1 | package base64url 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | func Test(t *testing.T) { TestingT(t) } 10 | type TestSuite struct{} 11 | var _ = Suite(&TestSuite{}) 12 | 13 | func (s *TestSuite) TestEncode(c *C) { 14 | //given 15 | in:=[]byte{72,101,108,108,111,32,66,97,115,101,54,52,85,114,108,32,101,110,99,111,100,105,110,103,33} 16 | 17 | //when 18 | test:=Encode(in) 19 | 20 | //then 21 | c.Assert(test, Equals, "SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ") 22 | } 23 | 24 | func (s *TestSuite) TestDecode(c *C) { 25 | //when 26 | test,err := Decode("SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ") 27 | 28 | //then 29 | c.Assert(err, IsNil) 30 | c.Assert(test, DeepEquals, []byte{72,101,108,108,111,32,66,97,115,101,54,52,85,114,108,32,101,110,99,111,100,105,110,103,33}) 31 | } 32 | 33 | func (s *TestSuite) TestDecodeIllegalBase64String(c *C) { 34 | //when 35 | _,err := Decode("SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nQ") 36 | 37 | //then 38 | c.Assert(err,NotNil) 39 | fmt.Printf("err = %v\n",err) 40 | } -------------------------------------------------------------------------------- /base64url/base64url.go: -------------------------------------------------------------------------------- 1 | // package base64url provides base64url encoding/decoding support 2 | package base64url 3 | 4 | import ( 5 | "strings" 6 | "encoding/base64" 7 | ) 8 | 9 | // Decode decodes base64url string to byte array 10 | func Decode(data string) ([]byte,error) { 11 | data = strings.Replace(data, "-", "+", -1) // 62nd char of encoding 12 | data = strings.Replace(data, "_", "/", -1) // 63rd char of encoding 13 | 14 | switch(len(data) % 4) { // Pad with trailing '='s 15 | case 0: // no padding 16 | case 2: data+="==" // 2 pad chars 17 | case 3: data+="=" // 1 pad char 18 | } 19 | 20 | return base64.StdEncoding.DecodeString(data) 21 | } 22 | 23 | // Encode encodes given byte array to base64url string 24 | func Encode(data []byte) string { 25 | result := base64.StdEncoding.EncodeToString(data) 26 | result = strings.Replace(result, "+", "-", -1) // 62nd char of encoding 27 | result = strings.Replace(result, "/", "_", -1) // 63rd char of encoding 28 | result = strings.Replace(result, "=", "", -1) // Remove any trailing '='s 29 | 30 | return result 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /aes/ecb_test.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import ( 4 | // "fmt" 5 | "crypto/aes" 6 | // "crypto/cipher" 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | func (s *TestSuite) TestNewECBEncryptor(c *C) { 11 | //given 12 | plaintext := []byte{0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255} 13 | kek := []byte{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 14 | block, _ := aes.NewCipher(kek) 15 | 16 | test := make([]byte,len(plaintext)) 17 | 18 | //when 19 | NewECBEncrypter(block).CryptBlocks(test,plaintext) 20 | 21 | //then 22 | c.Assert(test, DeepEquals, []byte{105,196,224,216,106,123,4,48,216,205,183,128,112,180,197,90}) 23 | } 24 | 25 | func (s *TestSuite) TestNewECBDecryptor(c *C) { 26 | //given 27 | ciphertext := []byte{105,196,224,216,106,123,4,48,216,205,183,128,112,180,197,90} 28 | kek := []byte{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 29 | block, _ := aes.NewCipher(kek) 30 | 31 | test := make([]byte,len(ciphertext)) 32 | 33 | //when 34 | NewECBDecrypter(block).CryptBlocks(test,ciphertext) 35 | 36 | //then 37 | c.Assert(test, DeepEquals, []byte{0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255}) 38 | } -------------------------------------------------------------------------------- /padding/pkcs7.go: -------------------------------------------------------------------------------- 1 | package padding 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // AddPkcs7 pads given byte array using pkcs7 padding schema till it has blockSize length in bytes 8 | func AddPkcs7(data []byte, blockSize int) []byte { 9 | 10 | var paddingCount int 11 | 12 | if paddingCount = blockSize - (len(data) % blockSize);paddingCount == 0 { 13 | paddingCount=blockSize 14 | } 15 | 16 | return append(data, bytes.Repeat([]byte{byte(paddingCount)}, paddingCount)...) 17 | } 18 | 19 | // RemovePkcs7 removes pkcs7 padding from previously padded byte array 20 | func RemovePkcs7(padded []byte, blockSize int) []byte { 21 | 22 | dataLen:=len(padded) 23 | paddingCount:=int(padded[dataLen-1]) 24 | 25 | if(paddingCount > blockSize || paddingCount <= 0) { 26 | return padded //data is not padded (or not padded correctly), return as is 27 | } 28 | 29 | padding := padded[dataLen-paddingCount : dataLen-1] 30 | 31 | for _, b := range padding { 32 | if int(b) != paddingCount { 33 | return padded //data is not padded (or not padded correcly), return as is 34 | } 35 | } 36 | 37 | return padded[:len(padded)-paddingCount] //return data - padding 38 | } -------------------------------------------------------------------------------- /hmac_using_sha.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/hmac" 5 | "errors" 6 | ) 7 | 8 | func init() { 9 | RegisterJws(&HmacUsingSha{keySizeBits: 256}) 10 | RegisterJws(&HmacUsingSha{keySizeBits: 384}) 11 | RegisterJws(&HmacUsingSha{keySizeBits: 512}) 12 | } 13 | 14 | // HMAC with SHA signing algorithm implementation 15 | type HmacUsingSha struct{ 16 | keySizeBits int 17 | } 18 | 19 | func (alg *HmacUsingSha) Name() string { 20 | switch alg.keySizeBits { 21 | case 256: return HS256 22 | case 384: return HS384 23 | default: return HS512 24 | } 25 | } 26 | 27 | func (alg *HmacUsingSha) Verify(securedInput, signature []byte, key interface{}) error { 28 | 29 | actualSig,_ := alg.Sign(securedInput, key) 30 | 31 | if !hmac.Equal(signature, actualSig) { 32 | return errors.New("HmacUsingSha.Verify(): Signature is invalid") 33 | } 34 | 35 | return nil 36 | } 37 | 38 | func (alg *HmacUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { 39 | //TODO: assert min key size 40 | 41 | if pubKey,ok:=key.([]byte); ok { 42 | return calculateHmac(alg.keySizeBits, securedInput, pubKey),nil 43 | } 44 | 45 | return nil,errors.New("HmacUsingSha.Sign(): expects key to be '[]byte' array") 46 | } -------------------------------------------------------------------------------- /kdf/nist_sp800_56a.go: -------------------------------------------------------------------------------- 1 | package kdf 2 | 3 | import ( 4 | "hash" 5 | "math" 6 | "github.com/dvsekhvalnov/jose2go/arrays" 7 | ) 8 | 9 | const ( 10 | MaxInt = int(^uint(0)>>1); 11 | ) 12 | 13 | // DeriveConcatKDF implements NIST SP 800-56A Concatenation Key Derivation Function. Derives 14 | // key material of keydatalen bits size given Z (sharedSecret), OtherInfo (AlgorithmID | 15 | // PartyUInfo | PartyVInfo | SuppPubInfo | SuppPrivInfo) and hash function 16 | func DeriveConcatKDF(keydatalen int, sharedSecret, algId, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo []byte, h hash.Hash) []byte { 17 | 18 | otherInfo := arrays.Concat(algId, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo) 19 | 20 | keyLenBytes := keydatalen >> 3 21 | 22 | reps := int(math.Ceil(float64(keyLenBytes) / float64(h.Size()))) 23 | 24 | if reps > MaxInt { 25 | panic("kdf.DeriveConcatKDF: too much iterations (more than 2^32-1).") 26 | } 27 | 28 | dk:=make([]byte, 0, keyLenBytes) 29 | 30 | for counter := 1;counter <= reps;counter++ { 31 | h.Reset() 32 | 33 | counterBytes:=arrays.UInt32ToBytes(uint32(counter)) 34 | 35 | h.Write(counterBytes) 36 | h.Write(sharedSecret) 37 | h.Write(otherInfo) 38 | 39 | dk = h.Sum(dk) 40 | } 41 | 42 | return dk[:keyLenBytes] 43 | } 44 | -------------------------------------------------------------------------------- /rsa_pkcs1v15.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "errors" 5 | "crypto/rsa" 6 | "crypto/rand" 7 | "github.com/dvsekhvalnov/jose2go/arrays" 8 | ) 9 | 10 | func init() { 11 | RegisterJwa(new(RsaPkcs1v15)) 12 | } 13 | 14 | // RS-AES using PKCS #1 v1.5 padding key management algorithm implementation 15 | type RsaPkcs1v15 struct{ 16 | } 17 | 18 | func (alg *RsaPkcs1v15) Name() string { 19 | return RSA1_5 20 | } 21 | 22 | func (alg *RsaPkcs1v15) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 23 | if pubKey,ok:=key.(*rsa.PublicKey);ok { 24 | if cek,err = arrays.Random(cekSizeBits>>3);err==nil { 25 | encryptedCek,err=rsa.EncryptPKCS1v15(rand.Reader,pubKey,cek) 26 | return 27 | } 28 | 29 | return nil,nil,err 30 | } 31 | 32 | return nil,nil,errors.New("RsaPkcs1v15.WrapNewKey(): expected key to be '*rsa.PublicKey'") 33 | } 34 | 35 | func (alg *RsaPkcs1v15) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 36 | if privKey,ok:=key.(*rsa.PrivateKey);ok { 37 | return rsa.DecryptPKCS1v15(rand.Reader,privKey,encryptedCek) 38 | } 39 | 40 | return nil,errors.New("RsaPkcs1v15.Unwrap(): expected key to be '*rsa.PrivateKey'") 41 | } 42 | -------------------------------------------------------------------------------- /rsa_using_sha.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "errors" 7 | ) 8 | 9 | func init() { 10 | RegisterJws(&RsaUsingSha{keySizeBits: 256}) 11 | RegisterJws(&RsaUsingSha{keySizeBits: 384}) 12 | RegisterJws(&RsaUsingSha{keySizeBits: 512}) 13 | } 14 | 15 | // RSA using SHA signature algorithm implementation 16 | type RsaUsingSha struct{ 17 | keySizeBits int 18 | } 19 | 20 | func (alg *RsaUsingSha) Name() string { 21 | switch alg.keySizeBits { 22 | case 256: return RS256 23 | case 384: return RS384 24 | default: return RS512 25 | } 26 | } 27 | 28 | func (alg *RsaUsingSha) Verify(securedInput, signature []byte, key interface{}) error { 29 | 30 | if pubKey,ok:=key.(*rsa.PublicKey);ok { 31 | return rsa.VerifyPKCS1v15(pubKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), signature) 32 | } 33 | 34 | return errors.New("RsaUsingSha.Verify(): expects key to be '*rsa.PublicKey'") 35 | } 36 | 37 | func (alg *RsaUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { 38 | 39 | if privKey,ok:=key.(*rsa.PrivateKey);ok { 40 | return rsa.SignPKCS1v15(rand.Reader, privKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput)) 41 | } 42 | 43 | return nil,errors.New("RsaUsingSha.Sign(): expects key to be '*rsa.PrivateKey'") 44 | } 45 | 46 | func sha(keySizeBits int, input []byte) (hash []byte) { 47 | hasher := hashAlg(keySizeBits) 48 | hasher.Write(input) 49 | return hasher.Sum(nil) 50 | } -------------------------------------------------------------------------------- /rsapss_using_sha.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "errors" 7 | ) 8 | 9 | func init() { 10 | RegisterJws(&RsaPssUsingSha{keySizeBits: 256, saltSizeBytes: 32}) 11 | RegisterJws(&RsaPssUsingSha{keySizeBits: 384, saltSizeBytes: 48}) 12 | RegisterJws(&RsaPssUsingSha{keySizeBits: 512, saltSizeBytes: 64}) 13 | } 14 | 15 | // RSA with PSS using SHA signing algorithm implementation 16 | type RsaPssUsingSha struct{ 17 | keySizeBits int 18 | saltSizeBytes int 19 | } 20 | 21 | func (alg *RsaPssUsingSha) Name() string { 22 | switch alg.keySizeBits { 23 | case 256: return PS256 24 | case 384: return PS384 25 | default: return PS512 26 | } 27 | } 28 | 29 | func (alg *RsaPssUsingSha) Verify(securedInput, signature []byte, key interface{}) error { 30 | if pubKey,ok:=key.(*rsa.PublicKey);ok { 31 | return rsa.VerifyPSS(pubKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), signature, &rsa.PSSOptions{SaltLength:alg.saltSizeBytes}) 32 | } 33 | 34 | return errors.New("RsaPssUsingSha.Verify(): expects key to be '*rsa.PublicKey'") 35 | } 36 | 37 | func (alg *RsaPssUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { 38 | if privKey,ok:=key.(*rsa.PrivateKey);ok { 39 | return rsa.SignPSS(rand.Reader, privKey, hashFunc(alg.keySizeBits), sha(alg.keySizeBits, securedInput), &rsa.PSSOptions{SaltLength:alg.saltSizeBytes}) 40 | } 41 | 42 | return nil,errors.New("RsaPssUsingSha.Sign(): expects key to be '*rsa.PrivateKey'") 43 | } 44 | -------------------------------------------------------------------------------- /ecdh_aeskw.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | func init() { 4 | RegisterJwa(&EcdhAesKW{ keySizeBits: 128, aesKW: &AesKW{ keySizeBits: 128}, ecdh: &Ecdh{directAgreement:false}}) 5 | RegisterJwa(&EcdhAesKW{ keySizeBits: 192, aesKW: &AesKW{ keySizeBits: 192}, ecdh: &Ecdh{directAgreement:false}}) 6 | RegisterJwa(&EcdhAesKW{ keySizeBits: 256, aesKW: &AesKW{ keySizeBits: 256}, ecdh: &Ecdh{directAgreement:false}}) 7 | } 8 | 9 | // Elliptic curve Diffie–Hellman with AES Key Wrap key management algorithm implementation 10 | type EcdhAesKW struct{ 11 | keySizeBits int 12 | aesKW JwaAlgorithm 13 | ecdh JwaAlgorithm 14 | } 15 | 16 | func (alg *EcdhAesKW) Name() string { 17 | switch alg.keySizeBits { 18 | case 128: return ECDH_ES_A128KW 19 | case 192: return ECDH_ES_A192KW 20 | default: return ECDH_ES_A256KW 21 | } 22 | } 23 | 24 | func (alg *EcdhAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 25 | var kek []byte 26 | 27 | if kek,_,err=alg.ecdh.WrapNewKey(alg.keySizeBits, key, header);err!=nil { 28 | return nil,nil,err 29 | } 30 | 31 | return alg.aesKW.WrapNewKey(cekSizeBits,kek,header) 32 | } 33 | 34 | func (alg *EcdhAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 35 | var kek []byte 36 | 37 | if kek,err=alg.ecdh.Unwrap(nil, key, alg.keySizeBits, header);err!=nil { 38 | return nil,err 39 | } 40 | 41 | return alg.aesKW.Unwrap(encryptedCek,kek,cekSizeBits,header) 42 | } -------------------------------------------------------------------------------- /aes/ecb.go: -------------------------------------------------------------------------------- 1 | // Package aes contains provides AES Key Wrap and ECB mode implementations 2 | package aes 3 | 4 | import ( 5 | "crypto/cipher" 6 | ) 7 | 8 | type ecb struct { 9 | b cipher.Block 10 | } 11 | 12 | type ecbEncrypter ecb 13 | type ecbDecrypter ecb 14 | 15 | // NewECBEncrypter creates BlockMode for AES encryption in ECB mode 16 | func NewECBEncrypter(b cipher.Block) cipher.BlockMode { 17 | return &ecbEncrypter{b: b} 18 | } 19 | 20 | // NewECBDecrypter creates BlockMode for AES decryption in ECB mode 21 | func NewECBDecrypter(b cipher.Block) cipher.BlockMode { 22 | return &ecbDecrypter{b: b} 23 | } 24 | 25 | func (x *ecbEncrypter) BlockSize() int { return x.b.BlockSize() } 26 | func (x *ecbDecrypter) BlockSize() int { return x.b.BlockSize() } 27 | 28 | func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { 29 | bs := x.BlockSize() 30 | 31 | if len(src)%bs != 0 { 32 | panic("ecbDecrypter.CryptBlocks(): input not full blocks") 33 | } 34 | 35 | if len(dst) < len(src) { 36 | panic("ecbDecrypter.CryptBlocks(): output smaller than input") 37 | } 38 | 39 | if len(src) == 0 { 40 | return 41 | } 42 | 43 | for len(src) > 0 { 44 | x.b.Decrypt(dst, src) 45 | src = src[bs:] 46 | } 47 | } 48 | 49 | func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { 50 | bs := x.BlockSize() 51 | 52 | if len(src)%bs != 0 { 53 | panic("ecbEncrypter.CryptBlocks(): input not full blocks") 54 | } 55 | 56 | if len(dst) < len(src) { 57 | panic("ecbEncrypter.CryptBlocks(): output smaller than input") 58 | } 59 | 60 | if len(src) == 0 { 61 | return 62 | } 63 | 64 | for len(src) > 0 { 65 | x.b.Encrypt(dst, src) 66 | src = src[bs:] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /deflate.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "bytes" 5 | "compress/flate" 6 | "errors" 7 | "io" 8 | ) 9 | 10 | var ErrSizeExceeded = errors.New("Deflate stream size exceeded limit.") 11 | 12 | func init() { 13 | // 250Kb limited decompression buffer 14 | RegisterJwc(NewDeflate(250 * 1024)) 15 | } 16 | 17 | // Deflate compression algorithm implementation 18 | type Deflate struct { 19 | maxBufferSizeBytes int64 20 | } 21 | 22 | func NewDeflate(maxBufferSizeBytes int64) JwcAlgorithm { 23 | return &Deflate{ 24 | maxBufferSizeBytes: maxBufferSizeBytes, 25 | } 26 | } 27 | 28 | func (alg *Deflate) Name() string { 29 | return DEF 30 | } 31 | 32 | func (alg *Deflate) Compress(plainText []byte) []byte { 33 | var buf bytes.Buffer 34 | deflate, _ := flate.NewWriter(&buf, 8) //level=DEFLATED 35 | 36 | deflate.Write(plainText) 37 | deflate.Close() 38 | 39 | return buf.Bytes() 40 | } 41 | 42 | func (alg *Deflate) Decompress(compressedText []byte) ([]byte, error) { 43 | enflated, err := io.ReadAll( 44 | newMaxBytesReader(alg.maxBufferSizeBytes, 45 | flate.NewReader( 46 | bytes.NewReader(compressedText)))) 47 | 48 | return enflated, err 49 | } 50 | 51 | // Max bytes reader 52 | type maxBytesReader struct { 53 | reader io.Reader 54 | limit int64 55 | } 56 | 57 | func newMaxBytesReader(limit int64, r io.Reader) io.Reader { 58 | return &maxBytesReader{reader: r, limit: limit} 59 | } 60 | 61 | func (mbr *maxBytesReader) Read(p []byte) (n int, err error) { 62 | if mbr.limit <= 0 { 63 | return 0, ErrSizeExceeded 64 | } 65 | 66 | if int64(len(p)) > mbr.limit { 67 | p = p[0:mbr.limit] 68 | } 69 | 70 | n, err = mbr.reader.Read(p) 71 | mbr.limit -= int64(n) 72 | return 73 | } 74 | -------------------------------------------------------------------------------- /keys/rsa/rsa.go: -------------------------------------------------------------------------------- 1 | //package Rsa provides helpers for creating rsa leys 2 | package Rsa 3 | 4 | import ( 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "encoding/pem" 8 | "errors" 9 | ) 10 | 11 | // ReadPrivate loads rsa.PrivateKey from PKCS1 or PKCS8 blobs 12 | func ReadPrivate(raw []byte) (key *rsa.PrivateKey,err error) { 13 | var encoded *pem.Block 14 | 15 | if encoded, _ = pem.Decode(raw); encoded == nil { 16 | return nil, errors.New("Rsa.NewPrivate(): Key must be PEM encoded PKCS1 or PKCS8 private key") 17 | } 18 | 19 | var parsedKey interface{} 20 | 21 | if parsedKey,err=x509.ParsePKCS1PrivateKey(encoded.Bytes);err!=nil { 22 | if parsedKey, err = x509.ParsePKCS8PrivateKey(encoded.Bytes);err!=nil { 23 | return nil,err 24 | } 25 | } 26 | 27 | var ok bool 28 | 29 | if key,ok=parsedKey.(*rsa.PrivateKey);!ok { 30 | return nil, errors.New("Rsa.NewPrivate(): Key is not valid *rsa.PrivateKey") 31 | } 32 | 33 | return key,nil 34 | } 35 | 36 | // ReadPublic loads rsa.PublicKey from PKIX or PKCS1 X509 blobs 37 | func ReadPublic(raw []byte) (key *rsa.PublicKey,err error) { 38 | var encoded *pem.Block 39 | 40 | if encoded, _ = pem.Decode(raw); encoded == nil { 41 | return nil, errors.New("Rsa.NewPublic(): Key must be PEM encoded PKCS1 X509 certificate or PKIX public key") 42 | } 43 | 44 | var parsedKey interface{} 45 | var cert *x509.Certificate 46 | 47 | if parsedKey, err = x509.ParsePKIXPublicKey(encoded.Bytes); err != nil { 48 | if cert,err = x509.ParseCertificate(encoded.Bytes);err!=nil { 49 | return nil, err 50 | } 51 | 52 | parsedKey=cert.PublicKey 53 | } 54 | 55 | var ok bool 56 | 57 | if key, ok = parsedKey.(*rsa.PublicKey); !ok { 58 | return nil, errors.New("Rsa.NewPublic(): Key is not a valid RSA public key") 59 | } 60 | 61 | return key, nil 62 | } -------------------------------------------------------------------------------- /kdf/pbkdf2.go: -------------------------------------------------------------------------------- 1 | // package kdf contains implementations of various key derivation functions 2 | package kdf 3 | 4 | import ( 5 | "crypto/hmac" 6 | "fmt" 7 | "hash" 8 | "math" 9 | 10 | "github.com/dvsekhvalnov/jose2go/arrays" 11 | ) 12 | 13 | // DerivePBKDF2 implements Password Based Key Derivation Function 2, RFC 2898. Derives key of keyBitLength size, given password, salt, iteration count and hash function 14 | func DerivePBKDF2(password, salt []byte, iterationCount, keyBitLength int, h func() hash.Hash) []byte { 15 | 16 | prf := hmac.New(h, password) 17 | hLen := prf.Size() 18 | dkLen := keyBitLength >> 3 //size of derived key in bytes 19 | 20 | l := int(math.Ceil(float64(dkLen) / float64(hLen))) // l = CEIL (dkLen / hLen) 21 | r := dkLen - (l-1)*hLen 22 | 23 | // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. 24 | if dkLen > MaxInt { 25 | panic(fmt.Sprintf("kdf.DerivePBKDF2: expects derived key size to be not more that (2^32-1) bits, but was requested %v bits.", keyBitLength)) 26 | } 27 | 28 | dk := make([]byte, 0, dkLen) 29 | 30 | for i := 0; i < l; i++ { 31 | 32 | t := f(salt, iterationCount, i+1, prf) // T_l = F (P, S, c, l) 33 | 34 | if i == (l - 1) { 35 | t = t[:r] 36 | } // truncate last block to r bits 37 | 38 | dk = append(dk, t...) // DK = T_1 || T_2 || ... || T_l<0..r-1> 39 | } 40 | 41 | return dk 42 | } 43 | 44 | func f(salt []byte, iterationCount, blockIndex int, prf hash.Hash) []byte { 45 | 46 | prf.Reset() 47 | prf.Write(salt) 48 | prf.Write(arrays.UInt32ToBytes(uint32(blockIndex))) 49 | 50 | u := prf.Sum(nil) // U_1 = PRF (P, S || INT (i)) 51 | 52 | result := u 53 | 54 | for i := 2; i <= iterationCount; i++ { 55 | prf.Reset() 56 | prf.Write(u) 57 | 58 | u = prf.Sum(nil) // U_c = PRF (P, U_{c-1}) . 59 | result = arrays.Xor(result, u) // U_1 \xor U_2 \xor ... \xor U_c 60 | } 61 | 62 | return result 63 | } 64 | -------------------------------------------------------------------------------- /aeskw.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/dvsekhvalnov/jose2go/aes" 7 | "github.com/dvsekhvalnov/jose2go/arrays" 8 | ) 9 | 10 | func init() { 11 | RegisterJwa(&AesKW{ keySizeBits: 128}) 12 | RegisterJwa(&AesKW{ keySizeBits: 192}) 13 | RegisterJwa(&AesKW{ keySizeBits: 256}) 14 | } 15 | 16 | // AES Key Wrap key management algorithm implementation 17 | type AesKW struct { 18 | keySizeBits int 19 | } 20 | 21 | func (alg *AesKW) Name() string { 22 | switch alg.keySizeBits { 23 | case 128: return A128KW 24 | case 192: return A192KW 25 | default: return A256KW 26 | } 27 | } 28 | 29 | func (alg *AesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 30 | if kek,ok:=key.([]byte); ok { 31 | 32 | kekSizeBits := len(kek) << 3 33 | 34 | if kekSizeBits != alg.keySizeBits { 35 | return nil,nil, errors.New(fmt.Sprintf("AesKW.WrapNewKey(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, kekSizeBits)) 36 | } 37 | 38 | if cek,err = arrays.Random(cekSizeBits>>3);err==nil { 39 | encryptedCek,err=aes.KeyWrap(cek,kek) 40 | return 41 | } 42 | 43 | return nil,nil,err 44 | 45 | } 46 | 47 | return nil,nil,errors.New("AesKW.WrapNewKey(): expected key to be '[]byte' array") 48 | } 49 | 50 | func (alg *AesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 51 | 52 | if kek,ok:=key.([]byte); ok { 53 | 54 | kekSizeBits := len(kek) << 3 55 | 56 | if kekSizeBits != alg.keySizeBits { 57 | return nil,errors.New(fmt.Sprintf("AesKW.Unwrap(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) 58 | } 59 | 60 | return aes.KeyUnwrap(encryptedCek, kek) 61 | } 62 | 63 | return nil,errors.New("AesKW.Unwrap(): expected key to be '[]byte' array") 64 | } 65 | -------------------------------------------------------------------------------- /rsa_oaep.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/sha1" 7 | "crypto/sha256" 8 | "crypto/sha512" 9 | "errors" 10 | "hash" 11 | 12 | "github.com/dvsekhvalnov/jose2go/arrays" 13 | ) 14 | 15 | // RS-AES using OAEP key management algorithm implementation 16 | func init() { 17 | RegisterJwa(&RsaOaep{shaSizeBits: 1}) 18 | RegisterJwa(&RsaOaep{shaSizeBits: 256}) 19 | RegisterJwa(&RsaOaep{shaSizeBits: 384}) 20 | RegisterJwa(&RsaOaep{shaSizeBits: 512}) 21 | } 22 | 23 | type RsaOaep struct { 24 | shaSizeBits int 25 | } 26 | 27 | func (alg *RsaOaep) Name() string { 28 | switch alg.shaSizeBits { 29 | case 1: 30 | return RSA_OAEP 31 | case 256: 32 | return RSA_OAEP_256 33 | case 384: 34 | return RSA_OAEP_384 35 | default: 36 | return RSA_OAEP_512 37 | } 38 | } 39 | 40 | func (alg *RsaOaep) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 41 | if pubKey, ok := key.(*rsa.PublicKey); ok { 42 | if cek, err = arrays.Random(cekSizeBits >> 3); err == nil { 43 | encryptedCek, err = rsa.EncryptOAEP(alg.sha(), rand.Reader, pubKey, cek, nil) 44 | return 45 | } 46 | 47 | return nil, nil, err 48 | } 49 | 50 | return nil, nil, errors.New("RsaOaep.WrapNewKey(): expected key to be '*rsa.PublicKey'") 51 | } 52 | 53 | func (alg *RsaOaep) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 54 | if privKey, ok := key.(*rsa.PrivateKey); ok { 55 | return rsa.DecryptOAEP(alg.sha(), rand.Reader, privKey, encryptedCek, nil) 56 | } 57 | 58 | return nil, errors.New("RsaOaep.Unwrap(): expected key to be '*rsa.PrivateKey'") 59 | } 60 | 61 | func (alg *RsaOaep) sha() hash.Hash { 62 | switch alg.shaSizeBits { 63 | case 1: 64 | return sha1.New() 65 | case 256: 66 | return sha256.New() 67 | case 384: 68 | return sha512.New384() 69 | default: 70 | return sha512.New() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /arrays/arrays_test.go: -------------------------------------------------------------------------------- 1 | package arrays 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | 10 | type TestSuite struct{} 11 | 12 | var _ = Suite(&TestSuite{}) 13 | 14 | func (s *TestSuite) TestXor(c *C) { 15 | //given 16 | data := []byte{0xFF, 0x00, 0xF0, 0x0F, 0x55, 0xAA, 0xBB, 0xCC} 17 | 18 | //when 19 | test_1 := Xor(data, []byte{0x00, 0xFF, 0x0F, 0xF0, 0xAA, 0x55, 0x44, 0x33}) 20 | test_2 := Xor(data, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 21 | 22 | //then 23 | c.Assert(test_1, DeepEquals, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 24 | c.Assert(test_2, DeepEquals, []byte{0x00, 0xFF, 0x0F, 0xF0, 0xAA, 0x55, 0x44, 0x33}) 25 | } 26 | 27 | func (s *TestSuite) TestSlice(c *C) { 28 | //given 29 | data := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8} 30 | 31 | //when 32 | test := Slice(data, 3) 33 | 34 | //then 35 | c.Assert(len(test), Equals, 3) 36 | c.Assert(test[0], DeepEquals, []byte{0, 1, 2}) 37 | c.Assert(test[1], DeepEquals, []byte{3, 4, 5}) 38 | c.Assert(test[2], DeepEquals, []byte{6, 7, 8}) 39 | } 40 | 41 | func (s *TestSuite) TestConcat(c *C) { 42 | //given 43 | a := []byte{1, 2, 3} 44 | b := []byte{4, 5} 45 | d := []byte{6} 46 | e := []byte{} 47 | f := []byte{7, 8, 9, 10} 48 | 49 | //when 50 | test := Concat(a, b, d, e, f) 51 | 52 | //then 53 | c.Assert(test, DeepEquals, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 54 | } 55 | 56 | func (s *TestSuite) TestUnwrap(c *C) { 57 | //given 58 | a := []byte{1, 2, 3} 59 | b := []byte{4, 5} 60 | d := []byte{6} 61 | e := []byte{} 62 | f := []byte{7, 8, 9, 10} 63 | 64 | //when 65 | test := Unwrap([][]byte{a, b, d, e, f}) 66 | 67 | //then 68 | c.Assert(test, DeepEquals, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 69 | } 70 | 71 | func (s *TestSuite) TestUInt32ToBytes(c *C) { 72 | //then 73 | c.Assert(UInt32ToBytes(0xFF), DeepEquals, []byte{0x00, 0x00, 0x00, 0xFF}) 74 | c.Assert(UInt32ToBytes(0xFFFFFFFE), DeepEquals, []byte{0xff, 0xff, 0xff, 0xfe}) 75 | } 76 | 77 | func (s *TestSuite) TestUInt64ToBytes(c *C) { 78 | //then 79 | c.Assert(UInt64ToBytes(0xFF), DeepEquals, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}) 80 | c.Assert(UInt64ToBytes(0xFFFFFFFFFFFFFFFE), DeepEquals, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}) 81 | } 82 | -------------------------------------------------------------------------------- /ecdsa_using_sha.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/rand" 5 | "math/big" 6 | "crypto/ecdsa" 7 | "errors" 8 | "github.com/dvsekhvalnov/jose2go/arrays" 9 | "github.com/dvsekhvalnov/jose2go/padding" 10 | "fmt" 11 | ) 12 | 13 | func init() { 14 | RegisterJws(&EcdsaUsingSha{keySizeBits: 256, hashSizeBits: 256}) 15 | RegisterJws(&EcdsaUsingSha{keySizeBits: 384, hashSizeBits: 384}) 16 | RegisterJws(&EcdsaUsingSha{keySizeBits: 521, hashSizeBits: 512}) 17 | } 18 | 19 | // ECDSA signing algorithm implementation 20 | type EcdsaUsingSha struct{ 21 | keySizeBits int 22 | hashSizeBits int 23 | } 24 | 25 | func (alg *EcdsaUsingSha) Name() string { 26 | switch alg.keySizeBits { 27 | case 256: return ES256 28 | case 384: return ES384 29 | default: return ES512 30 | } 31 | } 32 | 33 | func (alg *EcdsaUsingSha) Verify(securedInput, signature []byte, key interface{}) error { 34 | 35 | if pubKey,ok:=key.(*ecdsa.PublicKey);ok { 36 | 37 | if sizeBits:=pubKey.Curve.Params().BitSize;sizeBits!=alg.keySizeBits { 38 | return errors.New(fmt.Sprintf("EcdsaUsingSha.Verify(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits,sizeBits)) 39 | } 40 | 41 | r:=new(big.Int).SetBytes(signature[:len(signature)/2]) 42 | s:=new(big.Int).SetBytes(signature[len(signature)/2:]) 43 | 44 | if ok:=ecdsa.Verify(pubKey, sha(alg.hashSizeBits, securedInput), r,s); ok { 45 | return nil 46 | } 47 | 48 | return errors.New("EcdsaUsingSha.Verify(): Signature is not valid.") 49 | } 50 | 51 | return errors.New("EcdsaUsingSha.Verify(): expects key to be '*ecdsa.PublicKey'") 52 | } 53 | 54 | func (alg *EcdsaUsingSha) Sign(securedInput []byte, key interface{}) (signature []byte, err error) { 55 | 56 | if privKey,ok := key.(*ecdsa.PrivateKey);ok { 57 | 58 | if sizeBits:=privKey.Curve.Params().BitSize;sizeBits!=alg.keySizeBits { 59 | return nil,errors.New(fmt.Sprintf("EcdsaUsingSha.Sign(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits,sizeBits)) 60 | } 61 | 62 | var r,s *big.Int 63 | 64 | if r,s,err = ecdsa.Sign(rand.Reader, privKey, sha(alg.hashSizeBits, securedInput));err==nil { 65 | 66 | rBytes:=padding.Align(r.Bytes(), alg.keySizeBits) 67 | sBytes:=padding.Align(s.Bytes(), alg.keySizeBits) 68 | 69 | return arrays.Concat(rBytes,sBytes),nil 70 | } 71 | 72 | return nil, err 73 | } 74 | 75 | return nil,errors.New("EcdsaUsingSha.Sign(): expects key to be '*ecdsa.PrivateKey'") 76 | } -------------------------------------------------------------------------------- /padding/pkcs7_test.go: -------------------------------------------------------------------------------- 1 | package padding 2 | 3 | import ( 4 | "testing" 5 | . "gopkg.in/check.v1" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | type TestSuite struct{} 10 | var _ = Suite(&TestSuite{}) 11 | 12 | func (s *TestSuite) TestRemovePkcs7NoPadding(c *C) { 13 | //given 14 | padded:=[]byte{1,2,3,4,5,6,7,8} 15 | 16 | //when 17 | test:=RemovePkcs7(padded,8) 18 | 19 | //then 20 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,6,7,8}) 21 | } 22 | 23 | func (s *TestSuite) TestRemovePkcs7(c *C) { 24 | //given 25 | padded:=[]byte{1,2,3,4,5,3,3,3} 26 | 27 | //when 28 | test:=RemovePkcs7(padded,8) 29 | 30 | //then 31 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5}) 32 | } 33 | 34 | func (s *TestSuite) TestRemovePkcs7OneBytePadding(c *C) { 35 | //given 36 | padded:=[]byte{1,2,3,4,5,1} 37 | 38 | //when 39 | test:=RemovePkcs7(padded,6) 40 | 41 | //then 42 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5}) 43 | } 44 | 45 | func (s *TestSuite) TestRemovePkcs7TrailingZeroByte(c *C) { 46 | //given 47 | padded:=[]byte{1,2,3,4,5,0} 48 | 49 | //when 50 | test:=RemovePkcs7(padded,6) 51 | 52 | //then 53 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,0}) 54 | } 55 | 56 | func (s *TestSuite) TestRemovePkcs7ExtraBlockPadding(c *C) { 57 | //given 58 | padded:=[]byte{1,2,3,4,5,5,5,5,5,5} 59 | 60 | //when 61 | test:=RemovePkcs7(padded,5) 62 | 63 | //then 64 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5}) 65 | } 66 | 67 | func (s *TestSuite) TestRemovePkcs7TrailingByteGreaterBlockSize(c *C) { 68 | //given 69 | padded:=[]byte{1,2,3,4,5,10} 70 | 71 | //when 72 | test:=RemovePkcs7(padded,6) 73 | 74 | //then 75 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,10}) 76 | } 77 | 78 | func (s *TestSuite) TestAddPkcs7(c *C) { 79 | //given 80 | in:=[]byte{1,2,3,4,5} 81 | 82 | //when 83 | test := AddPkcs7(in,8) 84 | 85 | //then 86 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,3,3,3}) 87 | } 88 | 89 | func (s *TestSuite) TestAddPkcs7OneBytePadding(c *C) { 90 | //given 91 | in:=[]byte{1,2,3,4,5} 92 | 93 | //when 94 | test := AddPkcs7(in,6) 95 | 96 | //then 97 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,1}) 98 | } 99 | 100 | func (s *TestSuite) TestAddPkcs7ExtraBlockPadding(c *C) { 101 | //given 102 | in:=[]byte{1,2,3,4,5,6,7,8} 103 | 104 | //when 105 | test := AddPkcs7(in,8) 106 | 107 | //then 108 | c.Assert(test, DeepEquals, []byte{1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,8}) 109 | } -------------------------------------------------------------------------------- /kdf/nist_sp800_56a_test.go: -------------------------------------------------------------------------------- 1 | package kdf 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | "crypto/sha256" 6 | "github.com/dvsekhvalnov/jose2go/arrays" 7 | "github.com/dvsekhvalnov/jose2go/base64url" 8 | ) 9 | 10 | var z=[]byte{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31} 11 | var z2=[]byte{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47} 12 | 13 | var algId=prependDatalen([]byte("alg")) 14 | var partyUInfo=datalenFormat("pui") 15 | var partyVInfo=datalenFormat("pvi") 16 | 17 | func (s *TestSuite) Test256Secret_256Key(c *C) { 18 | //when 19 | test:=DeriveConcatKDF(256,z,algId,partyUInfo,partyVInfo,arrays.UInt32ToBytes(256),nil,sha256.New()) 20 | 21 | //then 22 | c.Assert(test, DeepEquals, []byte{190, 69, 15, 62, 38, 64, 30, 141, 208, 163, 55, 202, 18, 71, 176, 174, 114, 221, 249, 255, 207, 131, 190, 77, 12, 115, 220, 144, 102, 149, 78, 28}) 23 | } 24 | 25 | func (s *TestSuite) Test256Secret_384Key(c *C) { 26 | //when 27 | test:=DeriveConcatKDF(384,z,algId,partyUInfo,partyVInfo,arrays.UInt32ToBytes(384),nil,sha256.New()) 28 | 29 | //then 30 | c.Assert(test, DeepEquals, []byte{187, 16, 110, 253, 151, 199, 57, 235, 219, 8, 73, 191, 208, 108, 63, 241, 235, 137, 178, 149, 3, 199, 216, 99, 105, 217, 45, 114, 109, 213, 83, 198, 52, 101, 99, 146, 193, 31, 172, 157, 19, 140, 25, 202, 153, 92, 252, 192}) 31 | } 32 | 33 | func (s *TestSuite) Test256Secret_521Key(c *C) { 34 | //when 35 | test:=DeriveConcatKDF(521,z,algId,partyUInfo,partyVInfo,arrays.UInt32ToBytes(521),nil,sha256.New()) 36 | 37 | //then 38 | c.Assert(test, DeepEquals, []byte{245, 40, 159, 13, 25, 70, 170, 74, 210, 240, 242, 224, 37, 215, 23, 201, 126, 90, 108, 103, 205, 180, 48, 193, 131, 40, 86, 183, 97, 144, 1, 150, 154, 186, 196, 127, 6, 19, 17, 230, 75, 144, 229, 195, 61, 240, 20, 173, 167, 159, 50, 103, 133, 177, 241, 145, 134, 84, 50, 246, 157, 252, 51, 24, 35}) 39 | } 40 | 41 | func (s *TestSuite) Test384Secret_256Key(c *C) { 42 | //when 43 | test:=DeriveConcatKDF(256,z2,algId,partyUInfo,partyVInfo,arrays.UInt32ToBytes(256),nil,sha256.New()) 44 | 45 | //then 46 | c.Assert(test, DeepEquals, []byte{27, 55, 33, 99, 20, 191, 202, 69, 84, 176, 250, 108, 99, 7, 91, 49, 200, 47, 219, 142, 190, 216, 197, 154, 235, 17, 76, 12, 165, 75, 201, 108}) 47 | } 48 | 49 | //test utils 50 | func datalenFormat(str string) []byte { 51 | bytes,_:=base64url.Decode(str) 52 | return prependDatalen(bytes) 53 | } 54 | 55 | func prependDatalen(bytes []byte) []byte { 56 | return arrays.Concat(arrays.UInt32ToBytes(uint32(len(bytes))),bytes) 57 | } -------------------------------------------------------------------------------- /aes_gcm.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "github.com/dvsekhvalnov/jose2go/arrays" 9 | ) 10 | 11 | // AES GCM authenticated encryption algorithm implementation 12 | type AesGcm struct{ 13 | keySizeBits int 14 | } 15 | 16 | func init() { 17 | RegisterJwe(&AesGcm{keySizeBits:128}) 18 | RegisterJwe(&AesGcm{keySizeBits:192}) 19 | RegisterJwe(&AesGcm{keySizeBits:256}) 20 | } 21 | 22 | func (alg *AesGcm) Name() string { 23 | switch alg.keySizeBits { 24 | case 128: return A128GCM 25 | case 192: return A192GCM 26 | default: return A256GCM 27 | } 28 | } 29 | 30 | func (alg *AesGcm) KeySizeBits() int { 31 | return alg.keySizeBits 32 | } 33 | 34 | func (alg *AesGcm) Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) { 35 | 36 | cekSizeBits := len(cek)<<3 37 | 38 | if cekSizeBits != alg.keySizeBits { 39 | return nil,nil,nil, errors.New(fmt.Sprintf("AesGcm.Encrypt(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, cekSizeBits)) 40 | } 41 | 42 | if iv,err = arrays.Random(12);err!=nil { 43 | return nil,nil,nil,err 44 | } 45 | 46 | var block cipher.Block 47 | 48 | if block, err = aes.NewCipher(cek);err!=nil { 49 | return nil,nil,nil,err 50 | } 51 | 52 | var aesgcm cipher.AEAD 53 | 54 | if aesgcm,err = cipher.NewGCM(block);err!=nil { 55 | return nil,nil,nil,err 56 | } 57 | 58 | cipherWithTag := aesgcm.Seal(nil, iv, plainText, aad) 59 | 60 | cipherText=cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] 61 | authTag=cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] 62 | 63 | return iv, cipherText, authTag, nil 64 | } 65 | 66 | func (alg *AesGcm) Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) { 67 | 68 | cekSizeBits := len(cek)<<3 69 | 70 | if cekSizeBits != alg.keySizeBits { 71 | return nil, errors.New(fmt.Sprintf("AesGcm.Decrypt(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, cekSizeBits)) 72 | } 73 | 74 | var block cipher.Block 75 | 76 | if block, err = aes.NewCipher(cek);err!=nil { 77 | return nil,err 78 | } 79 | 80 | var aesgcm cipher.AEAD 81 | 82 | if aesgcm,err = cipher.NewGCM(block);err!=nil { 83 | return nil,err 84 | } 85 | 86 | cipherWithTag:=append(cipherText,authTag...) 87 | 88 | if nonceSize := len(iv); nonceSize != aesgcm.NonceSize() { 89 | return nil, errors.New(fmt.Sprintf("AesGcm.Decrypt(): expected nonce of size %v bits, but was given %v bits.", aesgcm.NonceSize()<<3, nonceSize<<3)) 90 | } 91 | 92 | if plainText,err = aesgcm.Open(nil, iv, cipherWithTag, aad);err!=nil { 93 | return nil,err 94 | } 95 | 96 | return plainText,nil 97 | } 98 | 99 | -------------------------------------------------------------------------------- /keys/ecc/ecc.go: -------------------------------------------------------------------------------- 1 | //package ecc provides helpers for creating elliptic curve leys 2 | package ecc 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "crypto/elliptic" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "errors" 10 | "math/big" 11 | ) 12 | 13 | // ReadPublic loads ecdsa.PublicKey from given PKCS1 X509 or PKIX blobs 14 | func ReadPublic(raw []byte) (key *ecdsa.PublicKey, err error) { 15 | var encoded *pem.Block 16 | 17 | if encoded, _ = pem.Decode(raw); encoded == nil { 18 | return nil, errors.New("Ecc.ReadPublic(): Key must be PEM encoded PKCS1 X509 certificate or PKIX EC public key") 19 | } 20 | 21 | var parsedKey interface{} 22 | var cert *x509.Certificate 23 | 24 | if parsedKey, err = x509.ParsePKIXPublicKey(encoded.Bytes); err != nil { 25 | if cert, err = x509.ParseCertificate(encoded.Bytes); err != nil { 26 | return nil, err 27 | } 28 | 29 | parsedKey = cert.PublicKey 30 | } 31 | 32 | var ok bool 33 | 34 | if key, ok = parsedKey.(*ecdsa.PublicKey); !ok { 35 | return nil, errors.New("Ecc.ReadPublic(): Key is not a valid *ecdsa.PublicKey") 36 | } 37 | 38 | return key, nil 39 | } 40 | 41 | // ReadPrivate loads ecdsa.PrivateKey from given PKCS1 or PKCS8 blobs 42 | func ReadPrivate(raw []byte) (key *ecdsa.PrivateKey, err error) { 43 | var encoded *pem.Block 44 | 45 | if encoded, _ = pem.Decode(raw); encoded == nil { 46 | return nil, errors.New("Ecc.ReadPrivate(): Key must be PEM encoded PKCS1 or PKCS8 EC private key") 47 | } 48 | 49 | var parsedKey interface{} 50 | 51 | if parsedKey, err = x509.ParseECPrivateKey(encoded.Bytes); err != nil { 52 | if parsedKey, err = x509.ParsePKCS8PrivateKey(encoded.Bytes); err != nil { 53 | return nil, err 54 | } 55 | } 56 | 57 | var ok bool 58 | 59 | if key, ok = parsedKey.(*ecdsa.PrivateKey); !ok { 60 | return nil, errors.New("Ecc.ReadPrivate(): Key is not valid *ecdsa.PrivateKey") 61 | } 62 | 63 | return key, nil 64 | } 65 | 66 | // NewPublic constructs ecdsa.PublicKey from given (X,Y) 67 | func NewPublic(x, y []byte) *ecdsa.PublicKey { 68 | return &ecdsa.PublicKey{Curve: curve(len(x)), 69 | X: new(big.Int).SetBytes(x), 70 | Y: new(big.Int).SetBytes(y)} 71 | } 72 | 73 | // NewPrivate constructs ecdsa.PrivateKey from given (X,Y) and D 74 | func NewPrivate(x, y, d []byte) *ecdsa.PrivateKey { 75 | return &ecdsa.PrivateKey{D: new(big.Int).SetBytes(d), 76 | PublicKey: ecdsa.PublicKey{Curve: curve(len(x)), 77 | X: new(big.Int).SetBytes(x), 78 | Y: new(big.Int).SetBytes(y)}} 79 | } 80 | 81 | func curve(size int) elliptic.Curve { 82 | switch size { 83 | case 31, 32: 84 | return elliptic.P256() 85 | case 48: 86 | return elliptic.P384() 87 | case 65, 66: 88 | return elliptic.P521() //adjust for P-521 curve, which can be 65 or 66 bytes 89 | default: 90 | return nil //unsupported curve 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /arrays/arrays.go: -------------------------------------------------------------------------------- 1 | // Package arrays provides various byte array utilities 2 | package arrays 3 | 4 | import ( 5 | "bytes" 6 | "crypto/rand" 7 | "encoding/binary" 8 | "fmt" 9 | "github.com/dvsekhvalnov/jose2go/base64url" 10 | ) 11 | 12 | // Xor is doing byte by byte exclusive or of 2 byte arrays 13 | func Xor(left, right []byte) []byte { 14 | result := make([]byte, len(left)) 15 | 16 | for i := 0; i < len(left); i++ { 17 | result[i] = left[i] ^ right[i] 18 | } 19 | 20 | return result 21 | } 22 | 23 | // Slice is splitting input byte array into slice of subarrays. Each of count length. 24 | func Slice(arr []byte, count int) [][]byte { 25 | 26 | sliceCount := len(arr) / count 27 | result := make([][]byte, sliceCount) 28 | 29 | for i := 0; i < sliceCount; i++ { 30 | start := i * count 31 | end := i*count + count 32 | 33 | result[i] = arr[start:end] 34 | } 35 | 36 | return result 37 | } 38 | 39 | // Random generates byte array with random data of byteCount length 40 | func Random(byteCount int) ([]byte, error) { 41 | data := make([]byte, byteCount) 42 | 43 | if _, err := rand.Read(data); err != nil { 44 | return nil, err 45 | } 46 | 47 | return data, nil 48 | } 49 | 50 | // Concat combine several arrays into single one, resulting slice = A1 | A2 | A3 | ... | An 51 | func Concat(arrays ...[]byte) []byte { 52 | var result []byte = arrays[0] 53 | 54 | for _, arr := range arrays[1:] { 55 | result = append(result, arr...) 56 | } 57 | 58 | return result 59 | } 60 | 61 | // Unwrap same thing as Contact, just different interface, combines several array into single one 62 | func Unwrap(arrays [][]byte) []byte { 63 | var result []byte = arrays[0] 64 | 65 | for _, arr := range arrays[1:] { 66 | result = append(result, arr...) 67 | } 68 | 69 | return result 70 | } 71 | 72 | // UInt64ToBytes unwrap uint64 value to byte array of length 8 using big endian 73 | func UInt64ToBytes(value uint64) []byte { 74 | result := make([]byte, 8) 75 | binary.BigEndian.PutUint64(result, value) 76 | 77 | return result 78 | } 79 | 80 | // UInt32ToBytes unwrap uint32 value to byte array of length 4 using big endian 81 | func UInt32ToBytes(value uint32) []byte { 82 | result := make([]byte, 4) 83 | binary.BigEndian.PutUint32(result, value) 84 | 85 | return result 86 | } 87 | 88 | // Dump produces printable debug representation of byte array as string 89 | func Dump(arr []byte) string { 90 | var buf bytes.Buffer 91 | 92 | buf.WriteString("(") 93 | buf.WriteString(fmt.Sprintf("%v", len(arr))) 94 | buf.WriteString(" bytes)[") 95 | 96 | for idx, b := range arr { 97 | buf.WriteString(fmt.Sprintf("%v", b)) 98 | if idx != len(arr)-1 { 99 | buf.WriteString(", ") 100 | } 101 | } 102 | 103 | buf.WriteString("], Hex: [") 104 | 105 | for idx, b := range arr { 106 | buf.WriteString(fmt.Sprintf("%X", b)) 107 | if idx != len(arr)-1 { 108 | buf.WriteString(" ") 109 | } 110 | } 111 | 112 | buf.WriteString("], Base64Url:") 113 | buf.WriteString(base64url.Encode(arr)) 114 | 115 | return buf.String() 116 | } 117 | -------------------------------------------------------------------------------- /aes_cbc_hmac.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/hmac" 7 | "errors" 8 | "fmt" 9 | "github.com/dvsekhvalnov/jose2go/arrays" 10 | "github.com/dvsekhvalnov/jose2go/padding" 11 | ) 12 | 13 | // AES CBC with HMAC authenticated encryption algorithm implementation 14 | type AesCbcHmac struct { 15 | keySizeBits int 16 | } 17 | 18 | func init() { 19 | RegisterJwe(&AesCbcHmac{keySizeBits: 256}) 20 | RegisterJwe(&AesCbcHmac{keySizeBits: 384}) 21 | RegisterJwe(&AesCbcHmac{keySizeBits: 512}) 22 | } 23 | 24 | func (alg *AesCbcHmac) Name() string { 25 | switch alg.keySizeBits { 26 | case 256: 27 | return A128CBC_HS256 28 | case 384: 29 | return A192CBC_HS384 30 | default: 31 | return A256CBC_HS512 32 | } 33 | } 34 | 35 | func (alg *AesCbcHmac) KeySizeBits() int { 36 | return alg.keySizeBits 37 | } 38 | 39 | func (alg *AesCbcHmac) SetKeySizeBits(bits int) { 40 | alg.keySizeBits = bits 41 | } 42 | 43 | func (alg *AesCbcHmac) Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) { 44 | 45 | cekSizeBits := len(cek) << 3 46 | if cekSizeBits != alg.keySizeBits { 47 | return nil, nil, nil, errors.New(fmt.Sprintf("AesCbcHmac.Encrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) 48 | } 49 | 50 | hmacKey := cek[0 : len(cek)/2] 51 | aesKey := cek[len(cek)/2:] 52 | 53 | if iv, err = arrays.Random(16); err != nil { 54 | return nil, nil, nil, err 55 | } 56 | 57 | var block cipher.Block 58 | 59 | if block, err = aes.NewCipher(aesKey); err != nil { 60 | return nil, nil, nil, err 61 | } 62 | 63 | padded := padding.AddPkcs7(plainText, 16) 64 | 65 | cipherText = make([]byte, len(padded), cap(padded)) 66 | mode := cipher.NewCBCEncrypter(block, iv) 67 | mode.CryptBlocks(cipherText, padded) 68 | 69 | authTag = alg.computeAuthTag(aad, iv, cipherText, hmacKey) 70 | 71 | return iv, cipherText, authTag, nil 72 | } 73 | 74 | func (alg *AesCbcHmac) Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) { 75 | 76 | cekSizeBits := len(cek) << 3 77 | 78 | if cekSizeBits != alg.keySizeBits { 79 | return nil, errors.New(fmt.Sprintf("AesCbcHmac.Decrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) 80 | } 81 | 82 | hmacKey := cek[0 : len(cek)/2] 83 | aesKey := cek[len(cek)/2:] 84 | 85 | // Check MAC 86 | expectedAuthTag := alg.computeAuthTag(aad, iv, cipherText, hmacKey) 87 | 88 | if !hmac.Equal(expectedAuthTag, authTag) { 89 | return nil, errors.New("AesCbcHmac.Decrypt(): Authentication tag do not match.") 90 | } 91 | 92 | var block cipher.Block 93 | 94 | if block, err = aes.NewCipher(aesKey); err == nil { 95 | mode := cipher.NewCBCDecrypter(block, iv) 96 | 97 | var padded []byte = make([]byte, len(cipherText), cap(cipherText)) 98 | mode.CryptBlocks(padded, cipherText) 99 | 100 | return padding.RemovePkcs7(padded, 16), nil 101 | } 102 | 103 | return nil, err 104 | } 105 | 106 | func (alg *AesCbcHmac) computeAuthTag(aad []byte, iv []byte, cipherText []byte, hmacKey []byte) (signature []byte) { 107 | al := arrays.UInt64ToBytes(uint64(len(aad) << 3)) 108 | hmacInput := arrays.Concat(aad, iv, cipherText, al) 109 | hmac := calculateHmac(alg.keySizeBits, hmacInput, hmacKey) 110 | 111 | return hmac[0 : len(hmac)/2] 112 | } 113 | -------------------------------------------------------------------------------- /aes_gcm_kw.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/dvsekhvalnov/jose2go/base64url" 7 | "github.com/dvsekhvalnov/jose2go/arrays" 8 | "crypto/aes" 9 | "crypto/cipher" 10 | ) 11 | 12 | func init() { 13 | RegisterJwa(&AesGcmKW{ keySizeBits: 128}) 14 | RegisterJwa(&AesGcmKW{ keySizeBits: 192}) 15 | RegisterJwa(&AesGcmKW{ keySizeBits: 256}) 16 | } 17 | 18 | // AES GCM Key Wrap key management algorithm implementation 19 | type AesGcmKW struct { 20 | keySizeBits int 21 | } 22 | 23 | func (alg *AesGcmKW) Name() string { 24 | switch alg.keySizeBits { 25 | case 128: return A128GCMKW 26 | case 192: return A192GCMKW 27 | default: return A256GCMKW 28 | } 29 | } 30 | 31 | func (alg *AesGcmKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 32 | if kek,ok:=key.([]byte); ok { 33 | 34 | kekSizeBits := len(kek) << 3 35 | 36 | if kekSizeBits != alg.keySizeBits { 37 | return nil,nil, errors.New(fmt.Sprintf("AesGcmKW.WrapNewKey(): expected key of size %v bits, but was given %v bits.",alg.keySizeBits, kekSizeBits)) 38 | } 39 | 40 | if cek,err = arrays.Random(cekSizeBits>>3);err!=nil { 41 | return nil,nil,err 42 | } 43 | 44 | var iv []byte 45 | 46 | if iv,err = arrays.Random(12);err!=nil { 47 | return nil,nil,err 48 | } 49 | 50 | var block cipher.Block 51 | 52 | if block, err = aes.NewCipher(kek);err!=nil { 53 | return nil,nil,err 54 | } 55 | 56 | var aesgcm cipher.AEAD 57 | 58 | if aesgcm,err = cipher.NewGCM(block);err!=nil { 59 | return nil,nil,err 60 | } 61 | 62 | cipherWithTag := aesgcm.Seal(nil, iv, cek, nil) 63 | 64 | cipherText := cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] 65 | authTag := cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] 66 | 67 | header["iv"]=base64url.Encode(iv) 68 | header["tag"]=base64url.Encode(authTag) 69 | 70 | return cek,cipherText,nil 71 | } 72 | 73 | return nil,nil,errors.New("AesGcmKW.WrapNewKey(): expected key to be '[]byte' array") 74 | } 75 | 76 | func (alg *AesGcmKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 77 | if kek,ok:=key.([]byte); ok { 78 | 79 | kekSizeBits := len(kek) << 3 80 | 81 | if kekSizeBits != alg.keySizeBits { 82 | return nil,errors.New(fmt.Sprintf("AesGcmKW.Unwrap(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) 83 | } 84 | 85 | var iv,tag string 86 | 87 | if iv,ok = header["iv"].(string);!ok { 88 | return nil,errors.New("AesGcmKW.Unwrap(): expected 'iv' param in JWT header, but was not found.") 89 | } 90 | 91 | if tag,ok = header["tag"].(string);!ok { 92 | return nil,errors.New("AesGcmKW.Unwrap(): expected 'tag' param in JWT header, but was not found.") 93 | } 94 | 95 | var ivBytes,tagBytes []byte 96 | 97 | if ivBytes,err = base64url.Decode(iv);err!=nil { 98 | return nil,err 99 | } 100 | 101 | if tagBytes,err = base64url.Decode(tag);err!=nil { 102 | return nil,err 103 | } 104 | 105 | var block cipher.Block 106 | 107 | if block, err = aes.NewCipher(kek);err!=nil { 108 | return nil,err 109 | } 110 | 111 | var aesgcm cipher.AEAD 112 | 113 | if aesgcm,err = cipher.NewGCM(block);err!=nil { 114 | return nil,err 115 | } 116 | 117 | cipherAndTag:=append(encryptedCek,tagBytes...) 118 | 119 | if cek,err = aesgcm.Open(nil, ivBytes,cipherAndTag , nil);err!=nil { 120 | fmt.Printf("err = %v\n",err) 121 | return nil,err 122 | } 123 | 124 | return cek,nil 125 | } 126 | 127 | return nil,errors.New("AesGcmKW.Unwrap(): expected key to be '[]byte' array") 128 | } 129 | -------------------------------------------------------------------------------- /aes/key_wrap.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import ( 4 | "github.com/dvsekhvalnov/jose2go/arrays" 5 | "crypto/cipher" 6 | "crypto/aes" 7 | "crypto/hmac" 8 | "errors" 9 | ) 10 | 11 | var defaultIV=[]byte { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 } 12 | 13 | // KeyWrap encrypts provided key (CEK) with KEK key using AES Key Wrap (rfc 3394) algorithm 14 | func KeyWrap(cek,kek []byte) ([]byte,error) { 15 | // 1) Initialize variables 16 | a := defaultIV // Set A = IV, an initial value 17 | r := arrays.Slice(cek, 8) // For i = 1 to n 18 | // R[0][i] = P[i] 19 | n := uint64(len(r)) 20 | 21 | // 2) Calculate intermediate values. 22 | var j,i,t uint64 23 | 24 | for j = 0; j < 6; j++ { // For j = 0 to 5 25 | for i = 0; i < n; i++ { // For i=1 to n 26 | t = n * j + i + 1; 27 | b,e := aesEnc(kek, arrays.Concat(a, r[i])) // B=AES(K, A | R[i]) 28 | 29 | if e!=nil { return nil, e } 30 | 31 | a = b[:len(b)/2] // A=MSB(64,B) ^ t where t = (n*j)+i 32 | r[i] = b[len(b)/2:] // R[i] = LSB(64, B) 33 | a = arrays.Xor(a, arrays.UInt64ToBytes(t)) 34 | } 35 | } 36 | 37 | // 3) Output the results 38 | c := make([][]byte, n+1, n+1) 39 | c[0] = a; // Set C[0] = A 40 | for i = 1; i <= n; i++ { // For i = 1 to n 41 | c[i] = r[i - 1] // C[i] = R[i] 42 | } 43 | 44 | return arrays.Unwrap(c),nil 45 | } 46 | 47 | // KeyUnwrap decrypts previously encrypted key (CEK) with KEK key using AES Key Wrap (rfc 3394) algorithm 48 | func KeyUnwrap(encryptedCek, kek []byte) ([]byte,error) { 49 | // 1) Initialize variables 50 | c := arrays.Slice(encryptedCek, 8); 51 | a := c[0]; // Set A = C[0] 52 | r := make([][]byte,len(c) - 1); 53 | 54 | for i := 1; i < len(c); i++ { // For i = 1 to n 55 | r[i - 1] = c[i]; // R[i] = C[i] 56 | } 57 | 58 | n := uint64(len(r)) 59 | 60 | // 2) Calculate intermediate values 61 | var t,j uint64 62 | 63 | for j = 6; j > 0; j-- { // For j = 5 to 0 64 | for i := n; i > 0; i-- { // For i = n to 1 65 | t = n * (j-1) + i; 66 | a = arrays.Xor(a, arrays.UInt64ToBytes(t)) 67 | b,e := aesDec(kek, arrays.Concat(a, r[i-1])) // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i 68 | 69 | if e!=nil { return nil,e } 70 | 71 | a = b[:len(b)/2] // A = MSB(64, B) 72 | r[i-1] = b[len(b)/2:] // R[i] = LSB(64, B) 73 | } 74 | } 75 | 76 | // 3) Output the results 77 | if (!hmac.Equal(defaultIV, a)) { // If A is an appropriate initial value 78 | return nil, errors.New("aes.KeyUnwrap(): integrity check failed.") 79 | } 80 | 81 | // For i = 1 to n 82 | return arrays.Unwrap(r),nil // P[i] = R[i] 83 | 84 | } 85 | 86 | func aesEnc(kek, plainText []byte) (cipherText []byte, err error) { 87 | var block cipher.Block 88 | 89 | if block, err = aes.NewCipher(kek);err!=nil { 90 | return nil,err 91 | } 92 | 93 | cipherText = make([]byte, len(plainText)) 94 | 95 | NewECBEncrypter(block).CryptBlocks(cipherText,plainText) 96 | 97 | return cipherText,nil 98 | } 99 | 100 | func aesDec(kek, cipherText []byte) (plainText []byte,err error) { 101 | 102 | var block cipher.Block 103 | 104 | if block, err = aes.NewCipher(kek);err!=nil { 105 | return nil,err 106 | } 107 | 108 | plainText = make([]byte, len(cipherText)) 109 | 110 | NewECBDecrypter(block).CryptBlocks(plainText,cipherText) 111 | 112 | return plainText,nil 113 | } -------------------------------------------------------------------------------- /keys/rsa/rsa_test.go: -------------------------------------------------------------------------------- 1 | package Rsa 2 | 3 | import ( 4 | "io/ioutil" 5 | "math/big" 6 | "testing" 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | func Test(t *testing.T) { TestingT(t) } 11 | type TestSuite struct{} 12 | var _ = Suite(&TestSuite{}) 13 | 14 | 15 | func (s *TestSuite) TestNewPrivateRsaKeyPKCS1(c *C) { 16 | 17 | //given 18 | keyBytes, _ := ioutil.ReadFile("priv.pem") 19 | 20 | //when 21 | test,e := ReadPrivate(keyBytes) 22 | 23 | //then 24 | c.Assert(e, IsNil) 25 | c.Assert(test.D, DeepEquals, bigInt("124664500442337916425629948081980373708538939606058968438393904444884872144817323585651521088406960965803456218656807473171367526874204593887158860695954252405916280866383288176587587469870736553178754458686625418367566925639753001190058223163696966510495553421774149906056482201169970508069587759758018186673")) 26 | c.Assert(test.Primes, HasLen, 2) 27 | c.Assert(test.Primes[0], DeepEquals, bigInt("12153319500662601635346432957857245055929638609873673015885135318360261418965835817520733410832489021447922460160007366037402643276624674886502627435389593")) 28 | c.Assert(test.Primes[1], DeepEquals, bigInt("10891140096045830766355607974974880425030121636560867106153739255802668290122259699296919947637286459736524694263050343032461619592457541146877906031793883")) 29 | c.Assert(test.E, Equals, 65537) 30 | c.Assert(test.N, DeepEquals, bigInt("132363505313722155184876628715249052276006747427497555521215412160460427148722445294983292629743653314149228192824806664548107146354876411009845771623017501212568714555984634301944063168503260813720165931647377932654326833738173931173570212088015547040743996672392373035728084429156775233489529905624779259619")) 31 | } 32 | 33 | func (s *TestSuite) TestNewPrivateRsaKeyPKCS8(c *C) { 34 | 35 | //given 36 | keyBytes, _ := ioutil.ReadFile("priv.pem") 37 | 38 | //when 39 | test,e := ReadPrivate(keyBytes) 40 | 41 | //then 42 | c.Assert(e, IsNil) 43 | c.Assert(test.D, DeepEquals, bigInt("124664500442337916425629948081980373708538939606058968438393904444884872144817323585651521088406960965803456218656807473171367526874204593887158860695954252405916280866383288176587587469870736553178754458686625418367566925639753001190058223163696966510495553421774149906056482201169970508069587759758018186673")) 44 | c.Assert(test.Primes, HasLen, 2) 45 | c.Assert(test.Primes[0], DeepEquals, bigInt("12153319500662601635346432957857245055929638609873673015885135318360261418965835817520733410832489021447922460160007366037402643276624674886502627435389593")) 46 | c.Assert(test.Primes[1], DeepEquals, bigInt("10891140096045830766355607974974880425030121636560867106153739255802668290122259699296919947637286459736524694263050343032461619592457541146877906031793883")) 47 | c.Assert(test.E, Equals, 65537) 48 | c.Assert(test.N, DeepEquals, bigInt("132363505313722155184876628715249052276006747427497555521215412160460427148722445294983292629743653314149228192824806664548107146354876411009845771623017501212568714555984634301944063168503260813720165931647377932654326833738173931173570212088015547040743996672392373035728084429156775233489529905624779259619")) 49 | } 50 | 51 | func (s *TestSuite) TestNewPublicRsaKeyPKCS1(c *C) { 52 | 53 | //given 54 | keyBytes, _ := ioutil.ReadFile("pub.pem") 55 | 56 | //when 57 | test,e := ReadPublic(keyBytes) 58 | 59 | //then 60 | c.Assert(e, IsNil) 61 | c.Assert(test.E, Equals, 65537) 62 | c.Assert(test.N, DeepEquals, bigInt("132363505313722155184876628715249052276006747427497555521215412160460427148722445294983292629743653314149228192824806664548107146354876411009845771623017501212568714555984634301944063168503260813720165931647377932654326833738173931173570212088015547040743996672392373035728084429156775233489529905624779259619")) 63 | } 64 | 65 | func (s *TestSuite) TestNewPublicRsaKeyPKIX(c *C) { 66 | 67 | //given 68 | keyBytes, _ := ioutil.ReadFile("pub.key") 69 | 70 | //when 71 | test,e := ReadPublic(keyBytes) 72 | 73 | //then 74 | c.Assert(e, IsNil) 75 | c.Assert(test.E, Equals, 65537) 76 | c.Assert(test.N, DeepEquals, bigInt("132363505313722155184876628715249052276006747427497555521215412160460427148722445294983292629743653314149228192824806664548107146354876411009845771623017501212568714555984634301944063168503260813720165931647377932654326833738173931173570212088015547040743996672392373035728084429156775233489529905624779259619")) 77 | } 78 | 79 | //utils 80 | func bigInt(value string) *big.Int { 81 | i:=new (big.Int) 82 | i.SetString(value,10) 83 | 84 | return i 85 | } -------------------------------------------------------------------------------- /keys/ecc/ecc_test.go: -------------------------------------------------------------------------------- 1 | package ecc 2 | 3 | import ( 4 | "testing" 5 | . "gopkg.in/check.v1" 6 | "io/ioutil" 7 | "math/big" 8 | "crypto/elliptic" 9 | ) 10 | 11 | func Test(t *testing.T) { TestingT(t) } 12 | type TestSuite struct{} 13 | var _ = Suite(&TestSuite{}) 14 | 15 | func (s *TestSuite) TestNewPublic(c *C) { 16 | //given 17 | x:=[]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} 18 | y:=[]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} 19 | 20 | //when 21 | test:=NewPublic(x,y) 22 | 23 | //then 24 | c.Assert(test.X, DeepEquals, bigInt("2010878128539620107131539503221291822343443356718189500659356750794038206985")) 25 | c.Assert(test.Y, DeepEquals, bigInt("59457993017710823357637488495120101390437944162821778556218889662829000218677")) 26 | c.Assert(test.Curve, Equals, elliptic.P256()) 27 | } 28 | 29 | func (s *TestSuite) TestNewPrivate(c *C) { 30 | //given 31 | x:=[]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} 32 | y:=[]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} 33 | d:=[]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 } 34 | 35 | //when 36 | test:=NewPrivate(x,y,d) 37 | 38 | //then 39 | c.Assert(test.X, DeepEquals, bigInt("2010878128539620107131539503221291822343443356718189500659356750794038206985")) 40 | c.Assert(test.Y, DeepEquals, bigInt("59457993017710823357637488495120101390437944162821778556218889662829000218677")) 41 | c.Assert(test.D, DeepEquals, bigInt("19260228627344101198652694952536756709538941185117188878548538012226554651342")) 42 | c.Assert(test.Curve, Equals, elliptic.P256()) 43 | } 44 | 45 | func (s *TestSuite) TestReadPublicPKIX(c *C) { 46 | //given 47 | keyBytes, _ := ioutil.ReadFile("./ec_public.key") 48 | 49 | //when 50 | test,e := ReadPublic(keyBytes) 51 | 52 | //then 53 | c.Assert(e, IsNil) 54 | 55 | c.Assert(test.X, DeepEquals, bigInt("76939435694210362824363841832595476784225842365248086547769733757874741672069")) 56 | c.Assert(test.Y, DeepEquals, bigInt("80047042001812490693675653292813886154388201612539715595028491948003157744818")) 57 | c.Assert(test.Curve, Equals, elliptic.P256()) 58 | } 59 | 60 | func (s *TestSuite) TestReadPublicPKCS1(c *C) { 61 | //given 62 | keyBytes, _ := ioutil.ReadFile("./ec_cert.pem") 63 | 64 | //when 65 | test,e := ReadPublic(keyBytes) 66 | 67 | //then 68 | c.Assert(e, IsNil) 69 | 70 | c.Assert(test.X, DeepEquals, bigInt("76939435694210362824363841832595476784225842365248086547769733757874741672069")) 71 | c.Assert(test.Y, DeepEquals, bigInt("80047042001812490693675653292813886154388201612539715595028491948003157744818")) 72 | c.Assert(test.Curve, Equals, elliptic.P256()) 73 | } 74 | 75 | func (s *TestSuite) TestReadPrivatePKCS1(c *C) { 76 | //given 77 | keyBytes, _ := ioutil.ReadFile("./ec_private.key") 78 | 79 | //when 80 | test,e := ReadPrivate(keyBytes) 81 | 82 | //then 83 | c.Assert(e, IsNil) 84 | 85 | c.Assert(test.X, DeepEquals, bigInt("76939435694210362824363841832595476784225842365248086547769733757874741672069")) 86 | c.Assert(test.Y, DeepEquals, bigInt("80047042001812490693675653292813886154388201612539715595028491948003157744818")) 87 | c.Assert(test.D, DeepEquals, bigInt("7222604869653061109880849859470152714201198955914263913554931724612175399644")) 88 | c.Assert(test.Curve, Equals, elliptic.P256()) 89 | } 90 | 91 | func (s *TestSuite) TestReadPrivatePKCS8(c *C) { 92 | //given 93 | keyBytes, _ := ioutil.ReadFile("./ec_private.pem") 94 | 95 | //when 96 | test,e := ReadPrivate(keyBytes) 97 | 98 | //then 99 | c.Assert(e, IsNil) 100 | 101 | c.Assert(test.X, DeepEquals, bigInt("76939435694210362824363841832595476784225842365248086547769733757874741672069")) 102 | c.Assert(test.Y, DeepEquals, bigInt("80047042001812490693675653292813886154388201612539715595028491948003157744818")) 103 | c.Assert(test.D, DeepEquals, bigInt("7222604869653061109880849859470152714201198955914263913554931724612175399644")) 104 | c.Assert(test.Curve, Equals, elliptic.P256()) 105 | } 106 | 107 | //utils 108 | func bigInt(value string) *big.Int { 109 | i:=new (big.Int) 110 | i.SetString(value,10) 111 | 112 | return i 113 | } -------------------------------------------------------------------------------- /pbse2_hmac_aeskw.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/sha512" 6 | "errors" 7 | "fmt" 8 | "hash" 9 | 10 | "github.com/dvsekhvalnov/jose2go/arrays" 11 | "github.com/dvsekhvalnov/jose2go/base64url" 12 | "github.com/dvsekhvalnov/jose2go/kdf" 13 | ) 14 | 15 | func init() { 16 | RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 0)) 17 | RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 0)) 18 | RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 0)) 19 | } 20 | 21 | // PBSE2 with HMAC key management algorithm implementation 22 | type Pbse2HmacAesKW struct { 23 | keySizeBits int 24 | aesKW JwaAlgorithm 25 | maxIterations int64 26 | minIterations int64 27 | } 28 | 29 | func NewPbse2HmacAesKWAlg(keySize int, maxIters int64, minIters int64) JwaAlgorithm { 30 | switch keySize { 31 | case 128: 32 | return &Pbse2HmacAesKW{keySizeBits: 128, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 128}} 33 | case 192: 34 | return &Pbse2HmacAesKW{keySizeBits: 192, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 192}} 35 | default: 36 | return &Pbse2HmacAesKW{keySizeBits: 256, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 256}} 37 | } 38 | } 39 | 40 | func (alg *Pbse2HmacAesKW) Name() string { 41 | switch alg.keySizeBits { 42 | case 128: 43 | return PBES2_HS256_A128KW 44 | case 192: 45 | return PBES2_HS384_A192KW 46 | default: 47 | return PBES2_HS512_A256KW 48 | } 49 | } 50 | 51 | func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 52 | if passphrase, ok := key.(string); ok { 53 | 54 | algId := []byte(header["alg"].(string)) 55 | 56 | iterationCount := 8192 57 | var saltInput []byte 58 | 59 | if saltInput, err = arrays.Random(12); err != nil { 60 | return nil, nil, err 61 | } 62 | 63 | // use user provided iteration counts if any 64 | if p2c, ok := header["p2c"].(int); ok { 65 | iterationCount = p2c 66 | } 67 | 68 | if int64(iterationCount) > alg.maxIterations { 69 | return nil, nil, errors.New( 70 | fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, iterationCount)) 71 | } 72 | 73 | if int64(iterationCount) < alg.minIterations { 74 | return nil, nil, errors.New( 75 | fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, iterationCount)) 76 | } 77 | 78 | header["p2c"] = iterationCount 79 | header["p2s"] = base64url.Encode(saltInput) 80 | 81 | salt := arrays.Concat(algId, []byte{0}, saltInput) 82 | 83 | kek := kdf.DerivePBKDF2([]byte(passphrase), salt, iterationCount, alg.keySizeBits, alg.prf) 84 | return alg.aesKW.WrapNewKey(cekSizeBits, kek, header) 85 | } 86 | 87 | return nil, nil, errors.New("Pbse2HmacAesKW.WrapNewKey(): expected key to be 'string' array") 88 | } 89 | 90 | func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 91 | 92 | if passphrase, ok := key.(string); ok { 93 | 94 | var p2s string 95 | var p2c float64 96 | 97 | if p2c, ok = header["p2c"].(float64); !ok { 98 | return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2c' param in JWT header, but was not found.") 99 | } 100 | 101 | if int64(p2c) > alg.maxIterations { 102 | return nil, errors.New( 103 | fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, p2c)) 104 | } 105 | 106 | if int64(p2c) < alg.minIterations { 107 | return nil, errors.New( 108 | fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, p2c)) 109 | } 110 | 111 | if p2s, ok = header["p2s"].(string); !ok { 112 | return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found") 113 | } 114 | 115 | var saltInput []byte 116 | 117 | algId := []byte(header["alg"].(string)) 118 | 119 | if saltInput, err = base64url.Decode(p2s); err != nil { 120 | return nil, err 121 | } 122 | 123 | salt := arrays.Concat(algId, []byte{0}, saltInput) 124 | 125 | kek := kdf.DerivePBKDF2([]byte(passphrase), salt, int(p2c), alg.keySizeBits, alg.prf) 126 | 127 | return alg.aesKW.Unwrap(encryptedCek, kek, cekSizeBits, header) 128 | } 129 | 130 | return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected key to be 'string' array") 131 | } 132 | 133 | func (alg *Pbse2HmacAesKW) prf() hash.Hash { 134 | switch alg.keySizeBits { 135 | case 128: 136 | return sha256.New() 137 | case 192: 138 | return sha512.New384() 139 | default: 140 | return sha512.New() 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /ecdh.go: -------------------------------------------------------------------------------- 1 | package jose 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "crypto/sha256" 8 | "errors" 9 | "fmt" 10 | "github.com/dvsekhvalnov/jose2go/arrays" 11 | "github.com/dvsekhvalnov/jose2go/base64url" 12 | "github.com/dvsekhvalnov/jose2go/kdf" 13 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 14 | "github.com/dvsekhvalnov/jose2go/padding" 15 | "math/big" 16 | ) 17 | 18 | func init() { 19 | RegisterJwa(&Ecdh{directAgreement: true}) 20 | } 21 | 22 | // Elliptic curve Diffie–Hellman key management (key agreement) algorithm implementation 23 | type Ecdh struct { 24 | directAgreement bool 25 | } 26 | 27 | func (alg *Ecdh) Name() string { 28 | return ECDH_ES 29 | } 30 | 31 | func (alg *Ecdh) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { 32 | 33 | if pubKey, ok := key.(*ecdsa.PublicKey); ok { 34 | 35 | if _, ok := header[alg.idHeader()].(string); !ok { 36 | return nil, nil, errors.New(fmt.Sprintf("Ecdh.WrapNewKey(): expected '%v' param in JWT header, but was not found.", alg.idHeader())) 37 | } 38 | 39 | var d []byte 40 | var x, y *big.Int 41 | 42 | if d, x, y, err = elliptic.GenerateKey(pubKey.Curve, rand.Reader); err != nil { 43 | return nil, nil, err 44 | } 45 | 46 | ephemeral := ecc.NewPrivate(x.Bytes(), y.Bytes(), d) 47 | 48 | xBytes := padding.Align(x.Bytes(), pubKey.Curve.Params().BitSize) 49 | yBytes := padding.Align(y.Bytes(), pubKey.Curve.Params().BitSize) 50 | 51 | epk := map[string]string{ 52 | "kty": "EC", 53 | "x": base64url.Encode(xBytes), 54 | "y": base64url.Encode(yBytes), 55 | "crv": name(pubKey.Curve), 56 | } 57 | 58 | header["epk"] = epk 59 | 60 | return alg.deriveKey(pubKey, ephemeral, cekSizeBits, header), nil, nil 61 | } 62 | 63 | return nil, nil, errors.New("Ecdh.WrapNewKey(): expected key to be '*ecdsa.PublicKey'") 64 | } 65 | 66 | func (alg *Ecdh) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { 67 | 68 | if privKey, ok := key.(*ecdsa.PrivateKey); ok { 69 | 70 | var epk map[string]interface{} 71 | 72 | if epk, ok = header["epk"].(map[string]interface{}); !ok { 73 | return nil, errors.New("Ecdh.Unwrap(): expected 'epk' param in JWT header, but was not found.") 74 | } 75 | 76 | if _, ok := header[alg.idHeader()].(string); !ok { 77 | return nil, errors.New(fmt.Sprintf("Ecdh.Unwrap(): expected '%v' param in JWT header, but was not found.", alg.idHeader())) 78 | } 79 | 80 | var x, y, crv string 81 | var xBytes, yBytes []byte 82 | 83 | if x, ok = epk["x"].(string); !ok { 84 | return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'x' was not found.") 85 | } 86 | 87 | if y, ok = epk["y"].(string); !ok { 88 | return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'y' was not found.") 89 | } 90 | 91 | if crv, ok = epk["crv"].(string); !ok { 92 | return nil, errors.New("Ecdh.Unwrap(): expects 'epk' key to contain 'x','y' and 'crv' fields, but 'crv' was not found.") 93 | } 94 | 95 | if crv != "P-256" && crv != "P-384" && crv != "P-521" { 96 | return nil, errors.New(fmt.Sprintf("Ecdh.Unwrap(): unknown or unsupported curve %v", crv)) 97 | } 98 | 99 | if xBytes, err = base64url.Decode(x); err != nil { 100 | return nil, err 101 | } 102 | if yBytes, err = base64url.Decode(y); err != nil { 103 | return nil, err 104 | } 105 | 106 | pubKey := ecc.NewPublic(xBytes, yBytes) 107 | 108 | if !privKey.Curve.IsOnCurve(pubKey.X, pubKey.Y) { 109 | return nil, errors.New(fmt.Sprintf("Ephemeral public key received in header is invalid for reciever's private key.")) 110 | } 111 | 112 | return alg.deriveKey(pubKey, privKey, cekSizeBits, header), nil 113 | } 114 | 115 | return nil, errors.New("Ecdh.Unwrap(): expected key to be '*ecdsa.PrivateKey'") 116 | } 117 | 118 | func (alg *Ecdh) deriveKey(pubKey *ecdsa.PublicKey, privKey *ecdsa.PrivateKey, keySizeBits int, header map[string]interface{}) []byte { 119 | 120 | var enc, apv, apu []byte 121 | var err error 122 | 123 | enc = []byte(header[alg.idHeader()].(string)) 124 | 125 | if a, ok := header["apv"].(string); !ok { 126 | if apv, err = base64url.Decode(a); err != nil { 127 | apv = nil 128 | } 129 | } 130 | 131 | if a, ok := header["apu"].(string); !ok { 132 | if apu, err = base64url.Decode(a); err != nil { 133 | apu = nil 134 | } 135 | } 136 | 137 | z, _ := pubKey.Curve.ScalarMult(pubKey.X, pubKey.Y, privKey.D.Bytes()) 138 | zBytes := padding.Align(z.Bytes(), privKey.Curve.Params().BitSize) 139 | 140 | return kdf.DeriveConcatKDF(keySizeBits, zBytes, prependDatalen(enc), prependDatalen(apu), prependDatalen(apv), arrays.UInt32ToBytes(uint32(keySizeBits)), nil, sha256.New()) 141 | } 142 | 143 | func (alg *Ecdh) idHeader() string { 144 | if alg.directAgreement { 145 | return "enc" 146 | } 147 | 148 | return "alg" 149 | } 150 | 151 | func name(curve elliptic.Curve) string { 152 | return fmt.Sprintf("P-%v", curve.Params().BitSize) 153 | } 154 | 155 | func prependDatalen(bytes []byte) []byte { 156 | return arrays.Concat(arrays.UInt32ToBytes(uint32(len(bytes))), bytes) 157 | } 158 | -------------------------------------------------------------------------------- /kdf/pbkdf2_test.go: -------------------------------------------------------------------------------- 1 | package kdf 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/sha512" 6 | . "gopkg.in/check.v1" 7 | "testing" 8 | ) 9 | 10 | func Test(t *testing.T) { TestingT(t) } 11 | 12 | type TestSuite struct{} 13 | 14 | var _ = Suite(&TestSuite{}) 15 | 16 | var password = []byte("password") 17 | var salt = []byte("salt") 18 | 19 | func (s *TestSuite) TestDerivePbkdf2Sha256Count1(c *C) { 20 | //when 21 | test := DerivePBKDF2(password, salt, 1, 256, sha256.New) 22 | 23 | //then 24 | c.Assert(test, DeepEquals, []byte{18, 15, 182, 207, 252, 248, 179, 44, 67, 231, 34, 82, 86, 196, 248, 55, 168, 101, 72, 201, 44, 204, 53, 72, 8, 5, 152, 124, 183, 11, 225, 123}) 25 | } 26 | 27 | func (s *TestSuite) TestDerivePbkdf2Sha256Count2(c *C) { 28 | //when 29 | test := DerivePBKDF2(password, salt, 2, 256, sha256.New) 30 | 31 | //then 32 | c.Assert(test, DeepEquals, []byte{174, 77, 12, 149, 175, 107, 70, 211, 45, 10, 223, 249, 40, 240, 109, 208, 42, 48, 63, 142, 243, 194, 81, 223, 214, 226, 216, 90, 149, 71, 76, 67}) 33 | } 34 | 35 | func (s *TestSuite) TestDerivePbkdf2Sha256Count4096(c *C) { 36 | //when 37 | test := DerivePBKDF2(password, salt, 4096, 256, sha256.New) 38 | 39 | //then 40 | c.Assert(test, DeepEquals, []byte{197, 228, 120, 213, 146, 136, 200, 65, 170, 83, 13, 182, 132, 92, 76, 141, 150, 40, 147, 160, 1, 206, 78, 17, 164, 150, 56, 115, 170, 152, 19, 74}) 41 | } 42 | 43 | func (s *TestSuite) TestDerivePbkdf2Sha256Count4096Len320(c *C) { 44 | //when 45 | test := DerivePBKDF2([]byte("passwordPASSWORDpassword"), []byte("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, 320, sha256.New) 46 | 47 | //then 48 | c.Assert(test, DeepEquals, []byte{52, 140, 137, 219, 203, 211, 43, 47, 50, 216, 20, 184, 17, 110, 132, 207, 43, 23, 52, 126, 188, 24, 0, 24, 28, 78, 42, 31, 184, 221, 83, 225, 198, 53, 81, 140, 125, 172, 71, 233}) 49 | } 50 | 51 | func (s *TestSuite) TestDerivePbkdf2Sha384Count1Len384(c *C) { 52 | //when 53 | test := DerivePBKDF2(password, salt, 1, 384, sha512.New384) 54 | 55 | //then 56 | c.Assert(test, DeepEquals, []byte{192, 225, 79, 6, 228, 158, 50, 215, 63, 159, 82, 221, 241, 208, 197, 199, 25, 22, 9, 35, 54, 49, 218, 221, 118, 165, 103, 219, 66, 183, 134, 118, 179, 143, 200, 0, 204, 83, 221, 182, 66, 245, 199, 68, 66, 230, 43, 228}) 57 | } 58 | 59 | func (s *TestSuite) TestDerivePbkdf2Sha384Count2Len384(c *C) { 60 | //when 61 | test := DerivePBKDF2(password, salt, 2, 384, sha512.New384) 62 | 63 | //then 64 | c.Assert(test, DeepEquals, []byte{84, 247, 117, 198, 215, 144, 242, 25, 48, 69, 145, 98, 252, 83, 93, 191, 4, 169, 57, 24, 81, 39, 1, 106, 4, 23, 106, 7, 48, 198, 241, 244, 251, 72, 131, 42, 209, 38, 27, 170, 221, 44, 237, 213, 8, 20, 177, 200}) 65 | } 66 | 67 | func (s *TestSuite) TestDerivePbkdf2Sha384Count4096Len384(c *C) { 68 | //when 69 | test := DerivePBKDF2(password, salt, 4096, 384, sha512.New384) 70 | 71 | //then 72 | c.Assert(test, DeepEquals, []byte{85, 151, 38, 190, 56, 219, 18, 91, 200, 94, 215, 137, 95, 110, 60, 245, 116, 199, 160, 28, 8, 12, 52, 71, 219, 30, 138, 118, 118, 77, 235, 60, 48, 123, 148, 133, 63, 190, 66, 79, 100, 136, 197, 244, 241, 40, 150, 38}) 73 | } 74 | 75 | func (s *TestSuite) TestDerivePbkdf2Sha384Count4096Len768(c *C) { 76 | //when 77 | test := DerivePBKDF2([]byte("passwordPASSWORDpassword"), []byte("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, 768, sha512.New384) 78 | 79 | //then 80 | c.Assert(test, DeepEquals, []byte{129, 145, 67, 173, 102, 223, 154, 85, 37, 89, 185, 225, 49, 197, 42, 230, 197, 193, 176, 238, 209, 143, 77, 40, 59, 140, 92, 158, 174, 185, 43, 57, 44, 20, 124, 194, 210, 134, 157, 88, 255, 226, 247, 218, 19, 209, 95, 141, 146, 87, 33, 240, 237, 26, 250, 250, 36, 72, 13, 85, 207, 96, 96, 177, 127, 17, 42, 61, 231, 76, 174, 37, 253, 243, 86, 158, 36, 127, 41, 228, 219, 184, 68, 33, 132, 120, 34, 234, 153, 189, 32, 40, 60, 58, 37, 166}) 81 | } 82 | 83 | func (s *TestSuite) TestDerivePbkdf2Sha512Count1Len512(c *C) { 84 | //when 85 | test := DerivePBKDF2(password, salt, 1, 512, sha512.New) 86 | 87 | //then 88 | c.Assert(test, DeepEquals, []byte{134, 127, 112, 207, 26, 222, 2, 207, 243, 117, 37, 153, 163, 165, 61, 196, 175, 52, 199, 166, 105, 129, 90, 229, 213, 19, 85, 78, 28, 140, 242, 82, 192, 45, 71, 10, 40, 90, 5, 1, 186, 217, 153, 191, 233, 67, 192, 143, 5, 2, 53, 215, 214, 139, 29, 165, 94, 99, 247, 59, 96, 165, 127, 206}) 89 | } 90 | 91 | func (s *TestSuite) TestDerivePbkdf2Sha512Count2Len512(c *C) { 92 | //when 93 | test := DerivePBKDF2(password, salt, 2, 512, sha512.New) 94 | 95 | //then 96 | c.Assert(test, DeepEquals, []byte{225, 217, 193, 106, 166, 129, 112, 138, 69, 245, 199, 196, 226, 21, 206, 182, 110, 1, 26, 46, 159, 0, 64, 113, 63, 24, 174, 253, 184, 102, 213, 60, 247, 108, 171, 40, 104, 163, 155, 159, 120, 64, 237, 206, 79, 239, 90, 130, 190, 103, 51, 92, 119, 166, 6, 142, 4, 17, 39, 84, 242, 124, 207, 78}) 97 | } 98 | 99 | func (s *TestSuite) TestDerivePbkdf2Sha512Count4096Len512(c *C) { 100 | //when 101 | test := DerivePBKDF2(password, salt, 4096, 512, sha512.New) 102 | 103 | //then 104 | c.Assert(test, DeepEquals, []byte{209, 151, 177, 179, 61, 176, 20, 62, 1, 139, 18, 243, 209, 209, 71, 158, 108, 222, 189, 204, 151, 197, 192, 248, 127, 105, 2, 224, 114, 244, 87, 181, 20, 63, 48, 96, 38, 65, 179, 213, 92, 211, 53, 152, 140, 179, 107, 132, 55, 96, 96, 236, 213, 50, 224, 57, 183, 66, 162, 57, 67, 74, 242, 213}) 105 | } 106 | 107 | func (s *TestSuite) TestDerivePbkdf2Sha256Count4096Len1024(c *C) { 108 | //when 109 | test := DerivePBKDF2([]byte("passwordPASSWORDpassword"), []byte("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, 1024, sha512.New) 110 | 111 | //then 112 | c.Assert(test, DeepEquals, []byte{140, 5, 17, 244, 198, 229, 151, 198, 172, 99, 21, 216, 240, 54, 46, 34, 95, 60, 80, 20, 149, 186, 35, 184, 104, 192, 5, 23, 77, 196, 238, 113, 17, 91, 89, 249, 230, 12, 217, 83, 47, 163, 62, 15, 117, 174, 254, 48, 34, 92, 88, 58, 24, 108, 216, 43, 212, 218, 234, 151, 36, 163, 211, 184, 4, 247, 91, 221, 65, 73, 79, 163, 36, 202, 178, 75, 204, 104, 15, 179, 185, 106, 48, 207, 93, 33, 250, 195, 194, 135, 89, 19, 145, 159, 51, 153, 177, 217, 206, 126, 181, 76, 149, 186, 73, 17, 133, 150, 207, 116, 101, 113, 155, 190, 2, 196, 236, 171, 27, 21, 65, 41, 140, 50, 29, 19, 198, 246}) 113 | } 114 | -------------------------------------------------------------------------------- /compact/compact_test.go: -------------------------------------------------------------------------------- 1 | package compact 2 | 3 | import ( 4 | "fmt" 5 | . "gopkg.in/check.v1" 6 | "testing" 7 | ) 8 | 9 | func Test(t *testing.T) { TestingT(t) } 10 | 11 | type TestSuite struct{} 12 | 13 | var _ = Suite(&TestSuite{}) 14 | 15 | func (s *TestSuite) TestParseThreeParts(c *C) { 16 | //when 17 | test, err := Parse("eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E") 18 | 19 | //then 20 | c.Assert(err, IsNil) 21 | c.Assert(test, HasLen, 3) 22 | c.Assert(test[0], DeepEquals, []byte{123, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 44, 34, 99, 116, 121, 34, 58, 34, 116, 101, 120, 116, 92, 47, 112, 108, 97, 105, 110, 34, 125}) 23 | c.Assert(test[1], DeepEquals, []byte{123, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 125}) 24 | c.Assert(test[2], DeepEquals, []byte{114, 18, 40, 97, 106, 208, 48, 15, 23, 47, 153, 197, 207, 170, 11, 12, 156, 175, 128, 121, 54, 40, 14, 1, 172, 81, 171, 43, 41, 163, 11, 193}) 25 | } 26 | 27 | func (s *TestSuite) TestParseEmptyTrailingPart(c *C) { 28 | //when 29 | test, err := Parse("eyJhbGciOiJub25lIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.") 30 | 31 | //then 32 | c.Assert(err, IsNil) 33 | c.Assert(test, HasLen, 3) 34 | c.Assert(test[0], DeepEquals, []byte{123, 34, 97, 108, 103, 34, 58, 34, 110, 111, 110, 101, 34, 125}) 35 | c.Assert(test[1], DeepEquals, []byte{123, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 125}) 36 | c.Assert(test[2], DeepEquals, []byte{}) 37 | } 38 | 39 | func (s *TestSuite) TestParseFiveParts(c *C) { 40 | //when 41 | test, err := Parse("eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4R0NNIn0.FojyyzygtFOyNBjzqTRfr9HVHPrvtqbVUt9sXSuU59ZhLlzk7FrirryFnFGtj8YC9lx-IX156Ro9rBaJTCU_dfERd05DhPMffT40rdcDiLxfCLOY0E2PfsMyGQPhI6YtNBtf_sQjXWEBC59zH_VoswFAUstkvXY9eVVecoM-W9HFlIxwUXMVpEPtS96xZX5LMksDgJ9sYDTNa6EQOA0hfzw07fD_FFJShcueqJuoJjILYbad-AHbpnLTV4oTbFTYjskRxpEYQr9plFZsT4_xKiCU89slT9EFhmuaiUI_-NGdX-kNDyQZj2Vtid4LSOVv5kGxyygThuQb6wjr1AGe1g.O92pf8iqwlBIQmXA.YdGjkN7lzeKYIv743XlPRYTd3x4VA0xwa5WVoGf1hiHlhQuXGEg4Jv3elk4JoFJzgVuMMQMex8fpFFL3t5I4H9bH18pbrEo7wLXvGOsP971cuOOaXPxhX6qClkwx5qkWhcTbO_2AuJxzIaU9qBwtwWaxJm9axofAPYgYbdaMZkU4F5sFdaFY8IOe94wUA1Ocn_gxC_DYp9IEAyZut0j5RImmthPgiRO_0pK9OvusE_Xg3iGfdxu70x0KpoItuNwlEf0LUA.uP5jOGMxtDUiT6E3ubucBw") 42 | 43 | //then 44 | c.Assert(err, IsNil) 45 | c.Assert(test, HasLen, 5) 46 | c.Assert(test[0], DeepEquals, []byte{123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 65, 49, 95, 53, 34, 44, 34, 101, 110, 99, 34, 58, 34, 65, 49, 50, 56, 71, 67, 77, 34, 125}) 47 | c.Assert(test[1], DeepEquals, []byte{22, 136, 242, 203, 60, 160, 180, 83, 178, 52, 24, 243, 169, 52, 95, 175, 209, 213, 28, 250, 239, 182, 166, 213, 82, 223, 108, 93, 43, 148, 231, 214, 97, 46, 92, 228, 236, 90, 226, 174, 188, 133, 156, 81, 173, 143, 198, 2, 246, 92, 126, 33, 125, 121, 233, 26, 61, 172, 22, 137, 76, 37, 63, 117, 241, 17, 119, 78, 67, 132, 243, 31, 125, 62, 52, 173, 215, 3, 136, 188, 95, 8, 179, 152, 208, 77, 143, 126, 195, 50, 25, 3, 225, 35, 166, 45, 52, 27, 95, 254, 196, 35, 93, 97, 1, 11, 159, 115, 31, 245, 104, 179, 1, 64, 82, 203, 100, 189, 118, 61, 121, 85, 94, 114, 131, 62, 91, 209, 197, 148, 140, 112, 81, 115, 21, 164, 67, 237, 75, 222, 177, 101, 126, 75, 50, 75, 3, 128, 159, 108, 96, 52, 205, 107, 161, 16, 56, 13, 33, 127, 60, 52, 237, 240, 255, 20, 82, 82, 133, 203, 158, 168, 155, 168, 38, 50, 11, 97, 182, 157, 248, 1, 219, 166, 114, 211, 87, 138, 19, 108, 84, 216, 142, 201, 17, 198, 145, 24, 66, 191, 105, 148, 86, 108, 79, 143, 241, 42, 32, 148, 243, 219, 37, 79, 209, 5, 134, 107, 154, 137, 66, 63, 248, 209, 157, 95, 233, 13, 15, 36, 25, 143, 101, 109, 137, 222, 11, 72, 229, 111, 230, 65, 177, 203, 40, 19, 134, 228, 27, 235, 8, 235, 212, 1, 158, 214}) 48 | c.Assert(test[2], DeepEquals, []byte{59, 221, 169, 127, 200, 170, 194, 80, 72, 66, 101, 192}) 49 | c.Assert(test[3], DeepEquals, []byte{97, 209, 163, 144, 222, 229, 205, 226, 152, 34, 254, 248, 221, 121, 79, 69, 132, 221, 223, 30, 21, 3, 76, 112, 107, 149, 149, 160, 103, 245, 134, 33, 229, 133, 11, 151, 24, 72, 56, 38, 253, 222, 150, 78, 9, 160, 82, 115, 129, 91, 140, 49, 3, 30, 199, 199, 233, 20, 82, 247, 183, 146, 56, 31, 214, 199, 215, 202, 91, 172, 74, 59, 192, 181, 239, 24, 235, 15, 247, 189, 92, 184, 227, 154, 92, 252, 97, 95, 170, 130, 150, 76, 49, 230, 169, 22, 133, 196, 219, 59, 253, 128, 184, 156, 115, 33, 165, 61, 168, 28, 45, 193, 102, 177, 38, 111, 90, 198, 135, 192, 61, 136, 24, 109, 214, 140, 102, 69, 56, 23, 155, 5, 117, 161, 88, 240, 131, 158, 247, 140, 20, 3, 83, 156, 159, 248, 49, 11, 240, 216, 167, 210, 4, 3, 38, 110, 183, 72, 249, 68, 137, 166, 182, 19, 224, 137, 19, 191, 210, 146, 189, 58, 251, 172, 19, 245, 224, 222, 33, 159, 119, 27, 187, 211, 29, 10, 166, 130, 45, 184, 220, 37, 17, 253, 11, 80}) 50 | c.Assert(test[4], DeepEquals, []byte{184, 254, 99, 56, 99, 49, 180, 53, 34, 79, 161, 55, 185, 187, 156, 7}) 51 | } 52 | 53 | func (s *TestSuite) TestParseInvalidBase64Encoded(c *C) { 54 | //when 55 | test, err := Parse("eyJhbGciOiJub25lIn0.eyJo@#xsbyI6ICJ3b3JsZCJ9.") 56 | 57 | //then 58 | c.Assert(err, NotNil) 59 | fmt.Printf("\nerr=%v\n", err) 60 | c.Assert(test, IsNil) 61 | } 62 | 63 | func (s *TestSuite) TestParseEmptyMiddlePart(c *C) { 64 | //when 65 | test, err := Parse("eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..yVi-LdQQngN0C5WS.1McwSmhZzAtmmLp9y-OdnJwaJFo1nj_4ashmzl2LhubGf0Jl1OTEVJzsHZb7bkup7cGTkuxh6Vfv10ljHsjWf_URXoxP3stQqQeViVcuPV0y2Q_WHYzTNGZpmHGe-hM6gjDhyZyvu3yeXGFSvfPQmp9pWVOgDjI4RC0MQ83rzzn-rRdnZkznWjbmOPxwPrR72Qng0BISsEwbkPn4oO8-vlHkVmPpuDTaYzCT2ZR5K9JnIU8d8QdxEAGb7-s8GEJ1yqtd_w._umbK59DAKA3O89h15VoKQ") 66 | 67 | //then 68 | c.Assert(err, IsNil) 69 | c.Assert(test, HasLen, 5) 70 | c.Assert(test[0], DeepEquals, []byte{123, 34, 97, 108, 103, 34, 58, 34, 100, 105, 114, 34, 44, 34, 101, 110, 99, 34, 58, 34, 65, 49, 50, 56, 71, 67, 77, 34, 125}) 71 | c.Assert(test[1], DeepEquals, []byte{}) 72 | c.Assert(test[2], DeepEquals, []byte{201, 88, 190, 45, 212, 16, 158, 3, 116, 11, 149, 146}) 73 | c.Assert(test[3], DeepEquals, []byte{212, 199, 48, 74, 104, 89, 204, 11, 102, 152, 186, 125, 203, 227, 157, 156, 156, 26, 36, 90, 53, 158, 63, 248, 106, 200, 102, 206, 93, 139, 134, 230, 198, 127, 66, 101, 212, 228, 196, 84, 156, 236, 29, 150, 251, 110, 75, 169, 237, 193, 147, 146, 236, 97, 233, 87, 239, 215, 73, 99, 30, 200, 214, 127, 245, 17, 94, 140, 79, 222, 203, 80, 169, 7, 149, 137, 87, 46, 61, 93, 50, 217, 15, 214, 29, 140, 211, 52, 102, 105, 152, 113, 158, 250, 19, 58, 130, 48, 225, 201, 156, 175, 187, 124, 158, 92, 97, 82, 189, 243, 208, 154, 159, 105, 89, 83, 160, 14, 50, 56, 68, 45, 12, 67, 205, 235, 207, 57, 254, 173, 23, 103, 102, 76, 231, 90, 54, 230, 56, 252, 112, 62, 180, 123, 217, 9, 224, 208, 18, 18, 176, 76, 27, 144, 249, 248, 160, 239, 62, 190, 81, 228, 86, 99, 233, 184, 52, 218, 99, 48, 147, 217, 148, 121, 43, 210, 103, 33, 79, 29, 241, 7, 113, 16, 1, 155, 239, 235, 60, 24, 66, 117, 202, 171, 93, 255}) 74 | c.Assert(test[4], DeepEquals, []byte{254, 233, 155, 43, 159, 67, 0, 160, 55, 59, 207, 97, 215, 149, 104, 41}) 75 | } 76 | 77 | func (s *TestSuite) TestSerialize(c *C) { 78 | //when 79 | test := Serialize([]byte{123, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 44, 34, 99, 116, 121, 34, 58, 34, 116, 101, 120, 116, 92, 47, 112, 108, 97, 105, 110, 34, 125}, 80 | []byte{123, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 125}, 81 | []byte{114, 18, 40, 97, 106, 208, 48, 15, 23, 47, 153, 197, 207, 170, 11, 12, 156, 175, 128, 121, 54, 40, 14, 1, 172, 81, 171, 43, 41, 163, 11, 193}) 82 | 83 | //then 84 | c.Assert(test, Equals, "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E") 85 | } 86 | 87 | func (s *TestSuite) TestSerializeEmptyTrailingPart(c *C) { 88 | //when 89 | test := Serialize([]byte{123, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 44, 34, 99, 116, 121, 34, 58, 34, 116, 101, 120, 116, 92, 47, 112, 108, 97, 105, 110, 34, 125}, 90 | []byte{123, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 125}, 91 | []byte{}) 92 | 93 | //then 94 | c.Assert(test, Equals, "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.") 95 | } 96 | 97 | func (s *TestSuite) TestSerializeEmptyMiddlePart(c *C) { 98 | //when 99 | test := Serialize([]byte{123, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 44, 34, 99, 116, 121, 34, 58, 34, 116, 101, 120, 116, 92, 47, 112, 108, 97, 105, 110, 34, 125}, 100 | []byte{}, 101 | []byte{114, 18, 40, 97, 106, 208, 48, 15, 23, 47, 153, 197, 207, 170, 11, 12, 156, 175, 128, 121, 54, 40, 14, 1, 172, 81, 171, 43, 41, 163, 11, 193}) 102 | 103 | //then 104 | c.Assert(test, Equals, "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0..chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E") 105 | } 106 | -------------------------------------------------------------------------------- /sec_test/security_vulnerabilities_test.go: -------------------------------------------------------------------------------- 1 | package sec_test 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/rsa" 6 | "encoding/json" 7 | "fmt" 8 | "strings" 9 | "testing" 10 | "time" 11 | 12 | jose "github.com/dvsekhvalnov/jose2go" 13 | "github.com/dvsekhvalnov/jose2go/arrays" 14 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 15 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 16 | . "gopkg.in/check.v1" 17 | ) 18 | 19 | func Test(t *testing.T) { TestingT(t) } 20 | 21 | type SecurityTestSuite struct{} 22 | 23 | var _ = Suite(&SecurityTestSuite{}) 24 | 25 | var aes128Key = []byte{194, 164, 235, 6, 138, 248, 171, 239, 24, 216, 11, 22, 137, 199, 215, 133} 26 | 27 | func (s *SecurityTestSuite) Test_InvalidCurve(c *C) { 28 | // https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf 29 | // Attack exploits some ECDH implementations which do not check 30 | // that ephemeral public key is on the private key's curve. 31 | 32 | //given 33 | //JWT encrypted with attacker private key, which is equals to (reciever_pk mod 113) 34 | attackMod113 := "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZ1RsaTY1ZVRRN3otQmgxNDdmZjhLM203azJVaURpRzJMcFlrV0FhRkpDYyIsInkiOiJjTEFuakthNGJ6akQ3REpWUHdhOUVQclJ6TUc3ck9OZ3NpVUQta2YzMEZzIiwiY3J2IjoiUC0yNTYifX0.qGAdxtEnrV_3zbIxU2ZKrMWcejNltjA_dtefBFnRh9A2z9cNIqYRWg.pEA5kX304PMCOmFSKX_cEg.a9fwUrx2JXi1OnWEMOmZhXd94-bEGCH9xxRwqcGuG2AMo-AwHoljdsH5C_kcTqlXS5p51OB1tvgQcMwB5rpTxg.72CHiYFecyDvuUa43KKT6w" 35 | 36 | //JWT encrypted with attacker private key, which is equals to (reciever_pk mod 2447) 37 | attackMod2447 := "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiWE9YR1E5XzZRQ3ZCZzN1OHZDSS1VZEJ2SUNBRWNOTkJyZnFkN3RHN29RNCIsInkiOiJoUW9XTm90bk56S2x3aUNuZUprTElxRG5UTnc3SXNkQkM1M1ZVcVZqVkpjIiwiY3J2IjoiUC0yNTYifX0.UGb3hX3ePAvtFB9TCdWsNkFTv9QWxSr3MpYNiSBdW630uRXRBT3sxw.6VpU84oMob16DxOR98YTRw.y1UslvtkoWdl9HpugfP0rSAkTw1xhm_LbK1iRXzGdpYqNwIG5VU33UBpKAtKFBoA1Kk_sYtfnHYAvn-aes4FTg.UZPN8h7FcvA5MIOq-Pkj8A" 38 | 39 | //when 40 | test, _, err := jose.Decode(attackMod113, Ecc256()) 41 | 42 | //then 43 | c.Assert(err, NotNil) 44 | fmt.Printf("\nerr= %v\n", err) 45 | c.Assert(test, Equals, "") 46 | 47 | //when 48 | test, _, err = jose.Decode(attackMod2447, Ecc256()) 49 | 50 | //then 51 | c.Assert(err, NotNil) 52 | fmt.Printf("\nerr= %v\n", err) 53 | c.Assert(test, Equals, "") 54 | } 55 | 56 | func (s *SecurityTestSuite) Test_AAD_IntegerOverflow(c *C) { 57 | //Borrowed test case from https://bitbucket.org/b_c/jose4j/commits/b79e67c13c23 58 | 59 | cek := []byte{57, 188, 52, 101, 199, 208, 135, 76, 159, 67, 65, 71, 196, 136, 137, 113, 227, 232, 28, 1, 61, 157, 73, 156, 68, 103, 67, 250, 215, 162, 181, 161} 60 | 61 | aad := []byte{0, 1, 2, 3, 4, 5, 6, 7} 62 | plainText := make([]byte, 536870928, 536870928) 63 | 64 | //generate random plaintext 65 | for i := 0; i < len(plainText); i += 8 { 66 | bytes := arrays.UInt64ToBytes(uint64(i)) 67 | plainText[i] = bytes[0] 68 | plainText[i+1] = bytes[1] 69 | plainText[i+2] = bytes[2] 70 | plainText[i+3] = bytes[3] 71 | plainText[i+4] = bytes[4] 72 | plainText[i+5] = bytes[5] 73 | plainText[i+6] = bytes[6] 74 | plainText[i+7] = bytes[7] 75 | } 76 | 77 | enc := &jose.AesCbcHmac{} 78 | enc.SetKeySizeBits(256) 79 | 80 | iv, cipherText, authTag, _ := enc.Encrypt(aad, plainText, cek) 81 | 82 | // Now shift aad and ciphertext around so that HMAC doesn't change, 83 | // but the plaintext will change. 84 | 85 | buffer := arrays.Concat(aad, iv, cipherText) 86 | 87 | // Note that due to integer overflow 536870920 * 8 = 64 88 | newAadSize := 536870920 89 | 90 | newAad := buffer[0:newAadSize] 91 | newIv := buffer[newAadSize : newAadSize+16] 92 | newCipherText := buffer[newAadSize+16:] 93 | 94 | //decrypt shifted binary, it should fail, since content is different now 95 | test, err := enc.Decrypt(newAad, cek, newIv, newCipherText, authTag) 96 | 97 | //if we reach that point HMAC check was bypassed although the decrypted data is different 98 | 99 | c.Assert(err, NotNil) 100 | fmt.Printf("\nerr= %v\n", err) 101 | c.Assert(test, IsNil) 102 | } 103 | 104 | func (s *SecurityTestSuite) Test_DeflateBomb(c *C) { 105 | strU := strings.Repeat("U", 400000000) 106 | strUU := strings.Repeat("U", 100000000) 107 | 108 | payloadMap := map[string]string{ 109 | "U": strU, 110 | "UU": strUU, 111 | } 112 | 113 | payloadBytes, _ := json.Marshal(payloadMap) 114 | 115 | fmt.Println("Uncompressed payload length", len(payloadBytes)) 116 | test, _ := jose.Encrypt(string(payloadBytes), jose.RSA_OAEP, jose.A256GCM, PubKey(), jose.Zip(jose.DEF)) 117 | fmt.Println("Encoded & Compressed token length", len(test)) 118 | 119 | start := time.Now() 120 | payload, headers, err := jose.Decode(test, PrivKey()) 121 | timeElapsed := time.Since(start) 122 | fmt.Printf("The `decode` took %s\n", timeElapsed) 123 | 124 | c.Assert(payload, Equals, "") 125 | c.Assert(headers, IsNil) 126 | c.Assert(err, Equals, jose.ErrSizeExceeded) 127 | } 128 | 129 | func (s *SecurityTestSuite) Test_TruncatedAesGcmAuthTag(c *C) { 130 | token := "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..PEXf1goWOF0SZRe_.Zp3CHYq4ZqM3_opMIy25O50gmQzw_p-nCOiW2ROuQSv80-aD-78n8m103kgPRPCsOt7qrckDRGSDACOBZGr2WovzSC-dxIcW3EsPqtibueyh0p3FY43h-bcbhPzXBdjQPaNTCY0o26wcEV_4FzPYdE9_ngRFIUe_7Kby-E2CWYLFc5D9RO9TLGN5dpHL6l4SOGbNz8M0o4aQuyJv3BV1wj_KswqyVcKBHjm0eh6RmFhoERxWjvt5yeo83bzxTfReVWAxXw.AVLr7JE1r1uiUSLj" 131 | payload, headers, err := jose.Decode(token, aes128Key) 132 | 133 | c.Assert(payload, Equals, "") 134 | c.Assert(headers, IsNil) 135 | c.Assert(err, NotNil) 136 | } 137 | 138 | func Ecc256() *ecdsa.PrivateKey { 139 | return ecc.NewPrivate([]byte{193, 227, 73, 203, 97, 236, 112, 36, 140, 232, 1, 3, 76, 56, 52, 225, 184, 142, 190, 17, 97, 203, 37, 175, 56, 116, 31, 120, 95, 207, 196, 196}, 140 | []byte{123, 201, 103, 8, 239, 128, 149, 43, 83, 248, 210, 85, 95, 231, 43, 132, 30, 208, 69, 136, 98, 139, 29, 55, 138, 89, 73, 57, 80, 14, 201, 201}, 141 | []byte{84, 73, 131, 102, 144, 215, 92, 175, 41, 240, 221, 2, 157, 219, 49, 179, 221, 184, 171, 169, 210, 213, 21, 197, 1, 36, 101, 232, 23, 212, 169, 220}) 142 | } 143 | 144 | func PubKey() *rsa.PublicKey { 145 | key, _ := Rsa.ReadPublic([]byte(pubKey)) 146 | return key 147 | } 148 | 149 | func PrivKey() *rsa.PrivateKey { 150 | key, _ := Rsa.ReadPrivate([]byte(privKey)) 151 | return key 152 | } 153 | 154 | var pubKey = `-----BEGIN CERTIFICATE----- 155 | MIICnTCCAYUCBEReYeAwDQYJKoZIhvcNAQEFBQAwEzERMA8GA1UEAxMIand0LTIw 156 | NDgwHhcNMTQwMTI0MTMwOTE2WhcNMzQwMjIzMjAwMDAwWjATMREwDwYDVQQDEwhq 157 | d3QtMjA0ODCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhWb9KXmv45 158 | +TKOKhFJkrboZbpbKPJ9Yp12xKLXf8060KfStEStIX+7dCuAYylYWoqiGpuLVVUL 159 | 5JmHgXmK9TJpzv9Dfe3TAc/+35r8r9IYB2gXUOZkebty05R6PLY0RO/hs2ZhrOoz 160 | HMo+x216Gwz0CWaajcuiY5Yg1V8VvJ1iQ3rcRgZapk49RNX69kQrGS63gzj0gyHn 161 | Rtbqc/Ua2kobCA83nnznCom3AGinnlSN65AFPP5jmri0l79+4ZZNIerErSW96mUF 162 | 8jlJFZI1yJIbzbv73tL+y4i0+BvzsWBs6TkHAp4pinaI8zT+hrVQ2jD4fkJEiRN9 163 | lAqLPUd8CNkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAnqBw3UHOSSHtU7yMi1+H 164 | E+9119tMh7X/fCpcpOnjYmhW8uy9SiPBZBl1z6vQYkMPcURnDMGHdA31kPKICZ6G 165 | LWGkBLY3BfIQi064e8vWHW7zX6+2Wi1zFWdJlmgQzBhbr8pYh9xjZe6FjPwbSEuS 166 | 0uE8dWSWHJLdWsA4xNX9k3pr601R2vPVFCDKs3K1a8P/Xi59kYmKMjaX6vYT879y 167 | gWt43yhtGTF48y85+eqLdFRFANTbBFSzdRlPQUYa5d9PZGxeBTcg7UBkK/G+d6D5 168 | sd78T2ymwlLYrNi+cSDYD6S4hwZaLeEK6h7p/OoG02RBNuT4VqFRu5DJ6Po+C6Jh 169 | qQ== 170 | -----END CERTIFICATE-----` 171 | 172 | var privKey = `-----BEGIN RSA PRIVATE KEY----- 173 | MIIEpAIBAAKCAQEAqFZv0pea/jn5Mo4qEUmStuhlulso8n1inXbEotd/zTrQp9K0 174 | RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO/0N97dMBz/7fmvyv0hgHaBdQ 175 | 5mR5u3LTlHo8tjRE7+GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxG 176 | BlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8 177 | /mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu/ve0v7LiLT4G/OxYGzpOQcC 178 | nimKdojzNP6GtVDaMPh+QkSJE32UCos9R3wI2QIDAQABAoIBAQCUmHBvSkqUHaK/ 179 | IMU7q2FqOi0KWswDefEiJKQhRu9Wv5NOgW2FrfqDIXrDp7pg1dBezgeExHLX9v6d 180 | FAOTwbj9/m6t3+r6k6fm7gp+ao3dfD6VgPd12L2oXQ0t5NVQ1UUBJ4/QUWps9h90 181 | 3AP4vK/COG1P+CAw4DDeZi9TlwF/Pr7e492GXcLBAUJODA6538ED2nYw8xQcbzbA 182 | wr+w07UjRNimObtOfA0HCIpsx/6LkIqe6iGChisQNgt4yDd/fZ4GWOUIU1hqgK1P 183 | 6avVl7Q5Mk0PTi9t8ui1X4EEq6Uils45J5WkobuAnFkea/uKfs8Tn9bNrEoVWgdb 184 | fBHq/8bNAoGBANKmjpE9e+L0RtxP+u4FN5YDoKE+i96VR7ru8H6yBKMcnD2uf5mV 185 | RueEoL0FKHxlGBBo0dJWr1AIwpcPbTs3Dgx1/EQMZLg57QBZ7QcYETPiMwMvEM3k 186 | Zf3G4YFYwUwIQXMYPt1ckr+RncRcq0GiKPDsvzzyNS+BBSmR5onAXd7bAoGBAMyT 187 | 6ggyqmiR/UwBn87em+GjbfX6YqxHHaQBdWwnnRX0JlGTNCxt6zLTgCIYxF4AA7eR 188 | gfGTStwUJfAScjJirOe6Cpm1XDgxEQrT6oxAl17MR/ms/Z88WrT73G+4phVvDpVr 189 | JcK+CCESnRI8xGLOLMkCc+5NpLajqWCOf1H2J8NbAoGAKTWmTGmf092AA1euOmRQ 190 | 5IsfIIxQ5qGDn+FgsRh4acSOGE8L7WrTrTU4EOJyciuA0qz+50xIDbs4/j5pWx1B 191 | JVTrnhBin9vNLrVo9mtR6jmFS0ko226kOUpwEVLgtdQjobWLjtiuaMW+/Iw4gKWN 192 | ptxZ6T1lBD8UWHaPiEFW2+MCgYAmfSWoyS96YQ0QwbV5TDRzrTXA84yg8PhIpOWc 193 | pY9OVBLpghJs0XlQpK4UvCglr0cDwGJ8OsP4x+mjUzUc+aeiKURZSt/Ayqp0KQ6V 194 | uIlCEpjwBnXpAYfnSQNeGZVVrwFFZ1VBYFNTNZdLmRcxp6yRXN7G1ODKY9w4CFc3 195 | 6mHsxQKBgQCxEA+KAmmXxL++x/XOElOscz3vFHC4HbpHpOb4nywpE9vunnHE2WY4 196 | EEW9aZbF22jx0ESU2XJ1JlqffvfIEvHNb5tmBWn4HZEpPUHdaFNhb9WjkMuFaLzh 197 | cydwnEftq+3G0X3KSxp4p7R7afcnpNNqfneYODgoXxTQ4Q7ZyKo72A== 198 | -----END RSA PRIVATE KEY-----` 199 | -------------------------------------------------------------------------------- /aes/key_wrap_test.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import ( 4 | "testing" 5 | // "fmt" 6 | . "gopkg.in/check.v1" 7 | // "github.com/dvsekhvalnov/jose2go/arrays" 8 | ) 9 | 10 | func Test(t *testing.T) { TestingT(t) } 11 | type TestSuite struct{} 12 | var _ = Suite(&TestSuite{}) 13 | 14 | func (s *TestSuite) TestWrap_128Key_128Kek(c *C) { 15 | //given (Section 4.1) 16 | 17 | //000102030405060708090A0B0C0D0E0F 18 | kek := []byte{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} 19 | 20 | //00112233445566778899AABBCCDDEEFF 21 | key := []byte{0,17,34,51,68,85,102,119,136,153,170,187,204,221,238,255} 22 | 23 | //1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5 24 | expected := []byte{ 31, 166, 139, 10, 129, 18, 180, 71, 174, 243, 75, 216, 251, 90, 123, 130, 157, 62, 134,35, 113, 210, 207, 229} 25 | 26 | //when 27 | test,_ := KeyWrap(key, kek); 28 | 29 | //then 30 | c.Assert(test, DeepEquals, expected) 31 | } 32 | 33 | func (s *TestSuite) Test_Unwrap128Key_128Kek(c *C) { 34 | //given (Section 4.1) 35 | 36 | //000102030405060708090A0B0C0D0E0F 37 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } 38 | 39 | //00112233445566778899AABBCCDDEEFF 40 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 } 41 | 42 | //1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5 43 | key := []byte{ 31, 166, 139, 10, 129, 18, 180, 71, 174, 243, 75, 216, 251, 90, 123, 130, 157, 62, 134, 35, 113, 210, 207, 229 } 44 | 45 | //when 46 | test,_ := KeyUnwrap(key, kek) 47 | 48 | //then 49 | c.Assert(test, DeepEquals, expected) 50 | } 51 | 52 | func (s *TestSuite) Test_Wrap_128Key_192Kek(c *C) { 53 | //given (Section 4.2) 54 | 55 | //000102030405060708090A0B0C0D0E0F1011121314151617 56 | kek := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23} 57 | 58 | //00112233445566778899AABBCCDDEEFF 59 | key := []byte{0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255} 60 | 61 | //96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D 62 | expected := []byte{150, 119, 139, 37, 174, 108, 164, 53, 249, 43, 91, 151, 192, 80, 174, 210, 70, 138, 184, 161, 122, 216, 78, 93} 63 | 64 | //when 65 | test,_ := KeyWrap(key, kek); 66 | 67 | //then 68 | c.Assert(test, DeepEquals, expected) 69 | } 70 | 71 | func (s *TestSuite) Test_Unwrap128Key_192Kek(c *C) { 72 | //given (Section 4.2) 73 | 74 | //000102030405060708090A0B0C0D0E0F1011121314151617 75 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 } 76 | 77 | //00112233445566778899AABBCCDDEEFF 78 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 } 79 | 80 | //96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D 81 | key := []byte{ 150, 119, 139, 37, 174, 108, 164, 53, 249, 43, 91, 151, 192, 80, 174, 210, 70, 138, 184, 161, 122, 216, 78, 93 } 82 | 83 | //when 84 | test,_ := KeyUnwrap(key, kek) 85 | 86 | //then 87 | c.Assert(test, DeepEquals, expected) 88 | } 89 | 90 | func (s *TestSuite) Test_Wrap_128Key_256Kek(c *C) { 91 | //given (Section 4.3) 92 | 93 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 94 | kek := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31} 95 | 96 | //00112233445566778899AABBCCDDEEFF 97 | key := []byte{0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255} 98 | 99 | //64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7 100 | expected := []byte{100, 232, 195, 249, 206, 15, 91, 162, 99, 233, 119, 121, 5, 129, 138, 42, 147, 200, 25, 30, 125, 110, 138, 231} 101 | 102 | //when 103 | test,_ := KeyWrap(key, kek); 104 | 105 | //then 106 | c.Assert(test, DeepEquals, expected) 107 | } 108 | 109 | func (s *TestSuite) Test_Unwrap128Key_256Kek(c *C) { 110 | //given (Section 4.3) 111 | 112 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 113 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } 114 | 115 | //00112233445566778899AABBCCDDEEFF 116 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 } 117 | 118 | //64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7 119 | key := []byte{ 100, 232, 195, 249, 206, 15, 91, 162, 99, 233, 119, 121, 5, 129, 138, 42, 147, 200, 25, 30, 125, 110, 138, 231 } 120 | 121 | //when 122 | test,_ := KeyUnwrap(key, kek) 123 | 124 | //then 125 | c.Assert(test, DeepEquals, expected) 126 | } 127 | 128 | func (s *TestSuite) Test_Wrap_192Key_192Kek(c *C) { 129 | //given (Section 4.4) 130 | 131 | //000102030405060708090A0B0C0D0E0F1011121314151617 132 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 } 133 | 134 | //00112233445566778899AABBCCDDEEFF0001020304050607 135 | key := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7 } 136 | 137 | //031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2 138 | expected := []byte{ 3, 29, 51, 38, 78, 21, 211, 50, 104, 242, 78, 194, 96, 116, 62, 220, 225, 198, 199, 221, 238, 114, 90, 147, 107, 168, 20, 145, 92, 103, 98, 210 } 139 | 140 | //when 141 | test,_ := KeyWrap(key, kek); 142 | 143 | //then 144 | c.Assert(test, DeepEquals, expected) 145 | } 146 | 147 | func (s *TestSuite) Test_Unwrap192Key_192Kek(c *C) { 148 | //given (Section 4.4) 149 | 150 | //000102030405060708090A0B0C0D0E0F1011121314151617 151 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 } 152 | 153 | //00112233445566778899AABBCCDDEEFF0001020304050607 154 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7 } 155 | 156 | //031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2 157 | key := []byte{ 3, 29, 51, 38, 78, 21, 211, 50, 104, 242, 78, 194, 96, 116, 62, 220, 225, 198, 199, 221, 238, 114, 90, 147, 107, 168, 20, 145, 92, 103, 98, 210 } 158 | 159 | //when 160 | test,_ := KeyUnwrap(key, kek) 161 | 162 | //then 163 | c.Assert(test, DeepEquals, expected) 164 | } 165 | 166 | func (s *TestSuite) Test_Wrap_192Key_256Kek(c *C) { 167 | //given (Section 4.5) 168 | 169 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 170 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } 171 | 172 | //00112233445566778899AABBCCDDEEFF0001020304050607 173 | key := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7 } 174 | 175 | //A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1 176 | expected := []byte{ 168, 249, 188, 22, 18, 198, 139, 63, 246, 230, 244, 251, 227, 14, 113, 228, 118, 156, 139, 128, 163, 44, 184, 149, 140, 213, 209, 125, 107, 37, 77, 161 } 177 | 178 | //when 179 | test,_ := KeyWrap(key, kek); 180 | 181 | //then 182 | c.Assert(test, DeepEquals, expected) 183 | } 184 | 185 | func (s *TestSuite) Test_Unwrap192Key_256Kek(c *C) { 186 | //given (Section 4.5) 187 | 188 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 189 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } 190 | 191 | //00112233445566778899AABBCCDDEEFF0001020304050607 192 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7 } 193 | 194 | //A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1 195 | key := []byte{ 168, 249, 188, 22, 18, 198, 139, 63, 246, 230, 244, 251, 227, 14, 113, 228, 118, 156, 139, 128, 163, 44, 184, 149, 140, 213, 209, 125, 107, 37, 77, 161 } 196 | 197 | //when 198 | test,_ := KeyUnwrap(key, kek) 199 | 200 | //then 201 | c.Assert(test, DeepEquals, expected) 202 | } 203 | 204 | func (s *TestSuite) Test_Wrap_256Key_256Kek(c *C) { 205 | //given (Section 4.6) 206 | 207 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 208 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } 209 | 210 | //00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F 211 | key := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } 212 | 213 | //28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21 214 | expected := []byte{ 40, 201, 244, 4, 196, 184, 16, 244, 203, 204, 179, 92, 251, 135, 248, 38, 63, 87, 134, 226, 216, 14, 211, 38, 203, 199, 240, 231, 26, 153, 244, 59, 251, 152, 139, 155, 122, 2, 221, 33 } 215 | 216 | //when 217 | test,_ := KeyWrap(key, kek); 218 | 219 | //then 220 | c.Assert(test, DeepEquals, expected) 221 | } 222 | 223 | func (s *TestSuite) Test_Unwrap256Key_256Kek(c *C) { 224 | //given (Section 4.6) 225 | 226 | //000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 227 | kek := []byte{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } 228 | 229 | //00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F 230 | expected := []byte{ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } 231 | 232 | //28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21 233 | key := []byte{ 40, 201, 244, 4, 196, 184, 16, 244, 203, 204, 179, 92, 251, 135, 248, 38, 63, 87, 134, 226, 216, 14, 211, 38, 203, 199, 240, 231, 26, 153, 244, 59, 251, 152, 139, 155, 122, 2, 221, 33 } 234 | 235 | //when 236 | test,_ := KeyUnwrap(key, kek) 237 | 238 | //then 239 | c.Assert(test, DeepEquals, expected) 240 | } -------------------------------------------------------------------------------- /jose.go: -------------------------------------------------------------------------------- 1 | // Package jose provides high level functions for producing (signing, encrypting and 2 | // compressing) or consuming (decoding) Json Web Tokens using Java Object Signing and Encryption spec 3 | package jose 4 | 5 | import ( 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | 10 | "github.com/dvsekhvalnov/jose2go/compact" 11 | ) 12 | 13 | const ( 14 | NONE = "none" //plaintext (unprotected) without signature / encryption 15 | 16 | HS256 = "HS256" //HMAC using SHA-256 hash 17 | HS384 = "HS384" //HMAC using SHA-384 hash 18 | HS512 = "HS512" //HMAC using SHA-512 hash 19 | RS256 = "RS256" //RSASSA-PKCS-v1_5 using SHA-256 hash 20 | RS384 = "RS384" //RSASSA-PKCS-v1_5 using SHA-384 hash 21 | RS512 = "RS512" //RSASSA-PKCS-v1_5 using SHA-512 hash 22 | PS256 = "PS256" //RSASSA-PSS using SHA-256 hash 23 | PS384 = "PS384" //RSASSA-PSS using SHA-384 hash 24 | PS512 = "PS512" //RSASSA-PSS using SHA-512 hash 25 | ES256 = "ES256" //ECDSA using P-256 curve and SHA-256 hash 26 | ES384 = "ES384" //ECDSA using P-384 curve and SHA-384 hash 27 | ES512 = "ES512" //ECDSA using P-521 curve and SHA-512 hash 28 | 29 | A128CBC_HS256 = "A128CBC-HS256" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 256 bit key 30 | A192CBC_HS384 = "A192CBC-HS384" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 384 bit key 31 | A256CBC_HS512 = "A256CBC-HS512" //AES in CBC mode with PKCS #5 (NIST.800-38A) padding with HMAC using 512 bit key 32 | A128GCM = "A128GCM" //AES in GCM mode with 128 bit key 33 | A192GCM = "A192GCM" //AES in GCM mode with 192 bit key 34 | A256GCM = "A256GCM" //AES in GCM mode with 256 bit key 35 | 36 | DIR = "dir" //Direct use of pre-shared symmetric key 37 | RSA1_5 = "RSA1_5" //RSAES with PKCS #1 v1.5 padding, RFC 3447 38 | RSA_OAEP = "RSA-OAEP" //RSAES using Optimal Assymetric Encryption Padding, RFC 3447 39 | RSA_OAEP_256 = "RSA-OAEP-256" //RSAES using Optimal Assymetric Encryption Padding with SHA-256, RFC 3447 40 | RSA_OAEP_384 = "RSA-OAEP-384" //RSAES using Optimal Assymetric Encryption Padding with SHA-384, RFC 3447 41 | RSA_OAEP_512 = "RSA-OAEP-512" //RSAES using Optimal Assymetric Encryption Padding with SHA-512, RFC 3447 42 | A128KW = "A128KW" //AES Key Wrap Algorithm using 128 bit keys, RFC 3394 43 | A192KW = "A192KW" //AES Key Wrap Algorithm using 192 bit keys, RFC 3394 44 | A256KW = "A256KW" //AES Key Wrap Algorithm using 256 bit keys, RFC 3394 45 | A128GCMKW = "A128GCMKW" //AES GCM Key Wrap Algorithm using 128 bit keys 46 | A192GCMKW = "A192GCMKW" //AES GCM Key Wrap Algorithm using 192 bit keys 47 | A256GCMKW = "A256GCMKW" //AES GCM Key Wrap Algorithm using 256 bit keys 48 | PBES2_HS256_A128KW = "PBES2-HS256+A128KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 128 bit key 49 | PBES2_HS384_A192KW = "PBES2-HS384+A192KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 192 bit key 50 | PBES2_HS512_A256KW = "PBES2-HS512+A256KW" //Password Based Encryption using PBES2 schemes with HMAC-SHA and AES Key Wrap using 256 bit key 51 | ECDH_ES = "ECDH-ES" //Elliptic Curve Diffie Hellman key agreement 52 | ECDH_ES_A128KW = "ECDH-ES+A128KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 128 bit key 53 | ECDH_ES_A192KW = "ECDH-ES+A192KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 192 bit key 54 | ECDH_ES_A256KW = "ECDH-ES+A256KW" //Elliptic Curve Diffie Hellman key agreement with AES Key Wrap using 256 bit key 55 | 56 | DEF = "DEF" //DEFLATE compression, RFC 1951 57 | ) 58 | 59 | var jwsHashers = map[string]JwsAlgorithm{} 60 | var jweEncryptors = map[string]JweEncryption{} 61 | var jwaAlgorithms = map[string]JwaAlgorithm{} 62 | var jwcCompressors = map[string]JwcAlgorithm{} 63 | 64 | // RegisterJwe register new encryption algorithm 65 | func RegisterJwe(alg JweEncryption) { 66 | jweEncryptors[alg.Name()] = alg 67 | } 68 | 69 | // RegisterJwa register new key management algorithm 70 | func RegisterJwa(alg JwaAlgorithm) { 71 | jwaAlgorithms[alg.Name()] = alg 72 | } 73 | 74 | // RegisterJws register new signing algorithm 75 | func RegisterJws(alg JwsAlgorithm) { 76 | jwsHashers[alg.Name()] = alg 77 | } 78 | 79 | // RegisterJwc register new compression algorithm 80 | func RegisterJwc(alg JwcAlgorithm) { 81 | jwcCompressors[alg.Name()] = alg 82 | } 83 | 84 | // DeregisterJwa deregister existing key management algorithm 85 | func DeregisterJwa(alg string) JwaAlgorithm { 86 | jwa := jwaAlgorithms[alg] 87 | 88 | delete(jwaAlgorithms, alg) 89 | 90 | return jwa 91 | } 92 | 93 | // DeregisterJws deregister existing signing algorithm 94 | func DeregisterJws(alg string) JwsAlgorithm { 95 | jws := jwsHashers[alg] 96 | 97 | delete(jwsHashers, alg) 98 | 99 | return jws 100 | } 101 | 102 | // DeregisterJws deregister existing encryption algorithm 103 | func DeregisterJwe(alg string) JweEncryption { 104 | jwe := jweEncryptors[alg] 105 | 106 | delete(jweEncryptors, alg) 107 | 108 | return jwe 109 | } 110 | 111 | // DeregisterJwc deregister existing compression algorithm 112 | func DeregisterJwc(alg string) JwcAlgorithm { 113 | jwc := jwcCompressors[alg] 114 | 115 | delete(jwcCompressors, alg) 116 | 117 | return jwc 118 | } 119 | 120 | // JweEncryption is a contract for implementing encryption algorithm 121 | type JweEncryption interface { 122 | Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) 123 | Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) 124 | KeySizeBits() int 125 | Name() string 126 | } 127 | 128 | // JwaAlgorithm is a contract for implementing key management algorithm 129 | type JwaAlgorithm interface { 130 | WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) 131 | Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) 132 | Name() string 133 | } 134 | 135 | // JwsAlgorithm is a contract for implementing signing algorithm 136 | type JwsAlgorithm interface { 137 | Verify(securedInput, signature []byte, key interface{}) error 138 | Sign(securedInput []byte, key interface{}) (signature []byte, err error) 139 | Name() string 140 | } 141 | 142 | // JwcAlgorithm is a contract for implementing compression algorithm 143 | type JwcAlgorithm interface { 144 | Compress(plainText []byte) []byte 145 | Decompress(compressedText []byte) ([]byte, error) 146 | Name() string 147 | } 148 | 149 | func Zip(alg string) func(cfg *JoseConfig) { 150 | return func(cfg *JoseConfig) { 151 | cfg.CompressionAlg = alg 152 | } 153 | } 154 | 155 | func Header(name string, value interface{}) func(cfg *JoseConfig) { 156 | return func(cfg *JoseConfig) { 157 | cfg.Headers[name] = value 158 | } 159 | } 160 | 161 | func Headers(headers map[string]interface{}) func(cfg *JoseConfig) { 162 | return func(cfg *JoseConfig) { 163 | for k, v := range headers { 164 | cfg.Headers[k] = v 165 | } 166 | } 167 | } 168 | 169 | type JoseConfig struct { 170 | CompressionAlg string 171 | Headers map[string]interface{} 172 | } 173 | 174 | // 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) 175 | // Signing key is of different type for different signing alg, see specific 176 | // signing alg implementation documentation. 177 | // 178 | // It returns 3 parts signed JWT token as string and not nil error if something went wrong. 179 | func Sign(payload string, signingAlg string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { 180 | return SignBytes([]byte(payload), signingAlg, key, options...) 181 | } 182 | 183 | // 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) 184 | // Signing key is of different type for different signing alg, see specific 185 | // signing alg implementation documentation. 186 | // 187 | // It returns 3 parts signed JWT token as string and not nil error if something went wrong. 188 | func SignBytes(payload []byte, signingAlg string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { 189 | if signer, ok := jwsHashers[signingAlg]; ok { 190 | 191 | cfg := &JoseConfig{CompressionAlg: "", Headers: make(map[string]interface{})} 192 | 193 | //apply extra options 194 | for _, option := range options { 195 | option(cfg) 196 | } 197 | 198 | //make sure defaults and requires are managed by us 199 | cfg.Headers["alg"] = signingAlg 200 | 201 | paloadBytes := payload 202 | var header []byte 203 | var signature []byte 204 | 205 | if header, err = json.Marshal(cfg.Headers); err == nil { 206 | securedInput := []byte(compact.Serialize(header, paloadBytes)) 207 | 208 | if signature, err = signer.Sign(securedInput, key); err == nil { 209 | return compact.Serialize(header, paloadBytes, signature), nil 210 | } 211 | } 212 | 213 | return "", err 214 | } 215 | 216 | return "", errors.New(fmt.Sprintf("jwt.Sign(): unknown algorithm: '%v'", signingAlg)) 217 | } 218 | 219 | // 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. 220 | // Management key is of different type for different key management alg, see specific 221 | // key management alg implementation documentation. 222 | // 223 | // It returns 5 parts encrypted JWT token as string and not nil error if something went wrong. 224 | func Encrypt(payload string, alg string, enc string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { 225 | return EncryptBytes([]byte(payload), alg, enc, key, options...) 226 | } 227 | 228 | // 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. 229 | // Management key is of different type for different key management alg, see specific 230 | // key management alg implementation documentation. 231 | // 232 | // It returns 5 parts encrypted JWT token as string and not nil error if something went wrong. 233 | func EncryptBytes(payload []byte, alg string, enc string, key interface{}, options ...func(*JoseConfig)) (token string, err error) { 234 | 235 | cfg := &JoseConfig{CompressionAlg: "", Headers: make(map[string]interface{})} 236 | 237 | //apply extra options 238 | for _, option := range options { 239 | option(cfg) 240 | } 241 | 242 | //make sure required headers are managed by us 243 | cfg.Headers["alg"] = alg 244 | cfg.Headers["enc"] = enc 245 | 246 | byteContent := payload 247 | 248 | if cfg.CompressionAlg != "" { 249 | if zipAlg, ok := jwcCompressors[cfg.CompressionAlg]; ok { 250 | byteContent = zipAlg.Compress([]byte(payload)) 251 | cfg.Headers["zip"] = cfg.CompressionAlg 252 | } else { 253 | return "", errors.New(fmt.Sprintf("jwt.Compress(): Unknown compression method '%v'", cfg.CompressionAlg)) 254 | } 255 | 256 | } else { 257 | delete(cfg.Headers, "zip") //we not allow to manage 'zip' header manually for encryption 258 | } 259 | 260 | return encrypt(byteContent, cfg.Headers, key) 261 | } 262 | 263 | // This method is DEPRICATED and subject to be removed in next version. 264 | // Use Encrypt(..) with Zip option instead. 265 | // 266 | // 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. 267 | // Management key is of different type for different key management alg, see specific 268 | // key management alg implementation documentation. 269 | // 270 | // It returns 5 parts encrypted & compressed JWT token as string and not nil error if something went wrong. 271 | func Compress(payload string, alg string, enc string, zip string, key interface{}) (token string, err error) { 272 | 273 | if zipAlg, ok := jwcCompressors[zip]; ok { 274 | compressed := zipAlg.Compress([]byte(payload)) 275 | 276 | jwtHeader := map[string]interface{}{ 277 | "enc": enc, 278 | "alg": alg, 279 | "zip": zip, 280 | } 281 | 282 | return encrypt(compressed, jwtHeader, key) 283 | } 284 | 285 | return "", errors.New(fmt.Sprintf("jwt.Compress(): Unknown compression method '%v'", zip)) 286 | } 287 | 288 | // Decode verifies, decrypts and decompresses given JWT token using management key. 289 | // Management key is of different type for different key management or signing algorithms, see specific alg implementation documentation. 290 | // 291 | // Returns decoded payload as a string, headers and not nil error if something went wrong. 292 | func Decode(token string, key interface{}) (string, map[string]interface{}, error) { 293 | 294 | payload, headers, err := DecodeBytes(token, key) 295 | 296 | if err != nil { 297 | return "", nil, err 298 | } 299 | 300 | return string(payload), headers, nil 301 | } 302 | 303 | // Decode verifies, decrypts and decompresses given JWT token using management key. 304 | // Management key is of different type for different key management or signing algorithms, see specific alg implementation documentation. 305 | // 306 | // Returns decoded payload as a raw bytes, headers and not nil error if something went wrong. 307 | func DecodeBytes(token string, key interface{}) ([]byte, map[string]interface{}, error) { 308 | parts, err := compact.Parse(token) 309 | 310 | if err != nil { 311 | return nil, nil, err 312 | } 313 | 314 | if len(parts) == 3 { 315 | return verify(parts, key) 316 | } 317 | 318 | if len(parts) == 5 { 319 | return decrypt(parts, key) 320 | } 321 | 322 | return nil, nil, errors.New(fmt.Sprintf("jwt.DecodeBytes() expects token of 3 or 5 parts, but was given: %v parts", len(parts))) 323 | } 324 | 325 | func encrypt(payload []byte, jwtHeader map[string]interface{}, key interface{}) (token string, err error) { 326 | var ok bool 327 | var keyMgmtAlg JwaAlgorithm 328 | var encAlg JweEncryption 329 | 330 | alg := jwtHeader["alg"].(string) 331 | enc := jwtHeader["enc"].(string) 332 | 333 | if keyMgmtAlg, ok = jwaAlgorithms[alg]; !ok { 334 | return "", errors.New(fmt.Sprintf("jwt.encrypt(): Unknown key management algorithm '%v'", alg)) 335 | } 336 | 337 | if encAlg, ok = jweEncryptors[enc]; !ok { 338 | return "", errors.New(fmt.Sprintf("jwt.encrypt(): Unknown encryption algorithm '%v'", enc)) 339 | } 340 | 341 | var cek, encryptedCek, header, iv, cipherText, authTag []byte 342 | 343 | if cek, encryptedCek, err = keyMgmtAlg.WrapNewKey(encAlg.KeySizeBits(), key, jwtHeader); err != nil { 344 | return "", err 345 | } 346 | 347 | if header, err = json.Marshal(jwtHeader); err != nil { 348 | return "", err 349 | } 350 | 351 | if iv, cipherText, authTag, err = encAlg.Encrypt([]byte(compact.Serialize(header)), payload, cek); err != nil { 352 | return "", err 353 | } 354 | 355 | return compact.Serialize(header, encryptedCek, iv, cipherText, authTag), nil 356 | } 357 | 358 | func verify(parts [][]byte, key interface{}) (plainText []byte, headers map[string]interface{}, err error) { 359 | 360 | header, payload, signature := parts[0], parts[1], parts[2] 361 | 362 | secured := []byte(compact.Serialize(header, payload)) 363 | 364 | var jwtHeader map[string]interface{} 365 | 366 | if err = json.Unmarshal(header, &jwtHeader); err != nil { 367 | return nil, nil, err 368 | } 369 | 370 | if alg, ok := jwtHeader["alg"].(string); ok { 371 | if verifier, ok := jwsHashers[alg]; ok { 372 | if key, err = retrieveActualKey(jwtHeader, string(payload), key); err != nil { 373 | return nil, nil, err 374 | } 375 | 376 | if err = verifier.Verify(secured, signature, key); err == nil { 377 | return payload, jwtHeader, nil 378 | } 379 | 380 | return nil, nil, err 381 | } 382 | 383 | return nil, nil, errors.New(fmt.Sprintf("jwt.Decode(): Unknown algorithm: '%v'", alg)) 384 | } 385 | 386 | return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'alg' header is missing or of invalid type")) 387 | } 388 | 389 | func decrypt(parts [][]byte, key interface{}) (plainText []byte, headers map[string]interface{}, err error) { 390 | 391 | header, encryptedCek, iv, cipherText, authTag := parts[0], parts[1], parts[2], parts[3], parts[4] 392 | 393 | var jwtHeader map[string]interface{} 394 | 395 | if e := json.Unmarshal(header, &jwtHeader); e != nil { 396 | return nil, nil, e 397 | } 398 | 399 | var keyMgmtAlg JwaAlgorithm 400 | var encAlg JweEncryption 401 | var zipAlg JwcAlgorithm 402 | var cek, plainBytes []byte 403 | var ok bool 404 | var alg, enc string 405 | 406 | if alg, ok = jwtHeader["alg"].(string); !ok { 407 | return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'alg' header is missing or of invalid type")) 408 | } 409 | 410 | if enc, ok = jwtHeader["enc"].(string); !ok { 411 | return nil, nil, errors.New(fmt.Sprint("jwt.Decode(): required 'enc' header is missing or of invalid type")) 412 | } 413 | 414 | aad := []byte(compact.Serialize(header)) 415 | 416 | if keyMgmtAlg, ok = jwaAlgorithms[alg]; ok { 417 | if encAlg, ok = jweEncryptors[enc]; ok { 418 | 419 | if key, err = retrieveActualKey(jwtHeader, string(cipherText), key); err != nil { 420 | return nil, nil, err 421 | } 422 | 423 | if cek, err = keyMgmtAlg.Unwrap(encryptedCek, key, encAlg.KeySizeBits(), jwtHeader); err == nil { 424 | if plainBytes, err = encAlg.Decrypt(aad, cek, iv, cipherText, authTag); err == nil { 425 | 426 | if zip, compressed := jwtHeader["zip"].(string); compressed { 427 | 428 | if zipAlg, ok = jwcCompressors[zip]; !ok { 429 | return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown compression algorithm '%v'", zip)) 430 | } 431 | 432 | if plainBytes, err = zipAlg.Decompress(plainBytes); err != nil { 433 | return nil, nil, err 434 | } 435 | } 436 | 437 | return plainBytes, jwtHeader, nil 438 | } 439 | 440 | return nil, nil, err 441 | } 442 | 443 | return nil, nil, err 444 | } 445 | 446 | return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown encryption algorithm '%v'", enc)) 447 | } 448 | 449 | return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown key management algorithm '%v'", alg)) 450 | } 451 | 452 | func retrieveActualKey(headers map[string]interface{}, payload string, key interface{}) (interface{}, error) { 453 | if keyCallback, ok := key.(func(headers map[string]interface{}, payload string) interface{}); ok { 454 | result := keyCallback(headers, payload) 455 | 456 | if err, ok := result.(error); ok { 457 | return nil, err 458 | } 459 | 460 | return result, nil 461 | } 462 | 463 | return key, nil 464 | } 465 | 466 | func Alg(key interface{}, jws string) func(headers map[string]interface{}, payload string) interface{} { 467 | return func(headers map[string]interface{}, payload string) interface{} { 468 | alg := headers["alg"].(string) 469 | 470 | if jws == alg { 471 | return key 472 | } 473 | 474 | return errors.New("Expected alg to be '" + jws + "' but got '" + alg + "'") 475 | } 476 | } 477 | 478 | func Enc(key interface{}, jwa string, jwe string) func(headers map[string]interface{}, payload string) interface{} { 479 | return func(headers map[string]interface{}, payload string) interface{} { 480 | alg := headers["alg"].(string) 481 | enc := headers["enc"].(string) 482 | 483 | if jwa == alg && jwe == enc { 484 | return key 485 | } 486 | 487 | return errors.New("Expected alg to be '" + jwa + "' and enc to be '" + jwe + "' but got '" + alg + "' and '" + enc + "'") 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang (GO) Javascript Object Signing and Encryption (JOSE) and JSON Web Token (JWT) implementation 2 | 3 | [![GoDoc](https://godoc.org/github.com/dvsekhvalnov/jose2go?status.svg)](http://godoc.org/github.com/dvsekhvalnov/jose2go) 4 | 5 | Pure Golang (GO) library for generating, decoding and encrypting [JSON Web Tokens](https://tools.ietf.org/html/rfc7519). Zero dependency, relies only 6 | on standard library. 7 | 8 | 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. 9 | 10 | 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 11 | [jose-jwt](https://github.com/dvsekhvalnov/jose-jwt) libraries. 12 | 13 | 14 | ## Status 15 | Used in production. GA ready. Current version is 1.6. 16 | 17 | ## Important 18 | v1.8 added experimental RSA-OAEP-384 and RSA-OAEP-512 key management algorithms 19 | 20 | v1.7 introduced deflate decompression memory limits to avoid denial-of-service attacks aka 'deflate-bomb'. See [Customizing compression](#customizing-compression) section for details. 21 | 22 | v1.6 security tuning options 23 | 24 | v1.5 bug fix release 25 | 26 | v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no 27 | extra headers added by library automatically. To mimic pre 1.4 behaviour use: 28 | ```Go 29 | token, err := jose.Sign(..., jose.Header("typ", "JWT")) 30 | 31 | //or 32 | 33 | token, err := jose.Encrypt(..., jose.Header("typ", "JWT")) 34 | ``` 35 | 36 | v1.3 fixed potential Invalid Curve Attack on NIST curves within ECDH key management. 37 | Upgrade strongly recommended. 38 | 39 | v1.2 breaks `jose.Decode` interface by returning 3 values instead of 2. 40 | 41 | v1.2 deprecates `jose.Compress` method in favor of using configuration options to `jose.Encrypt`, 42 | the method will be removed in next release. 43 | 44 | ### Migration to v1.2 45 | Pre v1.2 decoding: 46 | 47 | ```Go 48 | payload,err := jose.Decode(token,sharedKey) 49 | ``` 50 | 51 | Should be updated to v1.2: 52 | 53 | ```Go 54 | payload, headers, err := jose.Decode(token,sharedKey) 55 | ``` 56 | 57 | Pre v1.2 compression: 58 | 59 | ```Go 60 | token,err := jose.Compress(payload,jose.DIR,jose.A128GCM,jose.DEF, key) 61 | ``` 62 | 63 | Should be update to v1.2: 64 | 65 | ```Go 66 | token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, key, jose.Zip(jose.DEF)) 67 | ``` 68 | 69 | ## Supported JWA algorithms 70 | 71 | **Signing** 72 | - HMAC signatures with HS256, HS384 and HS512. 73 | - RSASSA-PKCS1-V1_5 signatures with RS256, RS384 and RS512. 74 | - RSASSA-PSS signatures (probabilistic signature scheme with appendix) with PS256, PS384 and PS512. 75 | - ECDSA signatures with ES256, ES384 and ES512. 76 | - NONE (unprotected) plain text algorithm without integrity protection 77 | 78 | **Encryption** 79 | - RSAES OAEP (using SHA-1 and MGF1 with SHA-1) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 80 | - RSAES OAEP 256, 384, 512 (using SHA-256, 384, 512 and MGF1 with SHA-256, 384, 512) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 81 | - RSAES-PKCS1-V1_5 encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 82 | - A128KW, A192KW, A256KW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 83 | - A128GCMKW, A192GCMKW, A256GCMKW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 84 | - ECDH-ES with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 85 | - ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 86 | - PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM 87 | - Direct symmetric key encryption with pre-shared key A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM and A256GCM 88 | 89 | **Compression** 90 | - DEFLATE compression 91 | 92 | ## Installation 93 | ### Grab package from github 94 | `go get github.com/dvsekhvalnov/jose2go` or `go get -u github.com/dvsekhvalnov/jose2go` to update to latest version 95 | 96 | ### Import package 97 | ```Go 98 | import ( 99 | "github.com/dvsekhvalnov/jose2go" 100 | ) 101 | ``` 102 | 103 | ## Usage 104 | #### Creating Plaintext (unprotected) Tokens 105 | 106 | ```Go 107 | package main 108 | 109 | import ( 110 | "fmt" 111 | "github.com/dvsekhvalnov/jose2go" 112 | ) 113 | 114 | func main() { 115 | 116 | payload := `{"hello": "world"}` 117 | 118 | token,err := jose.Sign(payload,jose.NONE, nil) 119 | 120 | if(err==nil) { 121 | //go use token 122 | fmt.Printf("\nPlaintext = %v\n",token) 123 | } 124 | } 125 | ``` 126 | 127 | ### Creating signed tokens 128 | #### HS-256, HS-384 and HS-512 129 | Signing with HS256, HS384, HS512 expecting `[]byte` array key of corresponding length: 130 | 131 | ```Go 132 | package main 133 | 134 | import ( 135 | "fmt" 136 | "github.com/dvsekhvalnov/jose2go" 137 | ) 138 | 139 | func main() { 140 | 141 | payload := `{"hello": "world"}` 142 | 143 | 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} 144 | 145 | token,err := jose.Sign(payload,jose.HS256,key) 146 | 147 | if(err==nil) { 148 | //go use token 149 | fmt.Printf("\nHS256 = %v\n",token) 150 | } 151 | } 152 | ``` 153 | 154 | #### RS-256, RS-384 and RS-512, PS-256, PS-384 and PS-512 155 | 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. 156 | 157 | ```Go 158 | package main 159 | 160 | import ( 161 | "fmt" 162 | "io/ioutil" 163 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 164 | "github.com/dvsekhvalnov/jose2go" 165 | ) 166 | 167 | func main() { 168 | 169 | payload := `{"hello": "world"}` 170 | 171 | keyBytes,err := ioutil.ReadFile("private.key") 172 | 173 | if(err!=nil) { 174 | panic("invalid key file") 175 | } 176 | 177 | privateKey,e:=Rsa.ReadPrivate(keyBytes) 178 | 179 | if(e!=nil) { 180 | panic("invalid key format") 181 | } 182 | 183 | token,err := jose.Sign(payload,jose.RS256, privateKey) 184 | 185 | if(err==nil) { 186 | //go use token 187 | fmt.Printf("\nRS256 = %v\n",token) 188 | } 189 | } 190 | ``` 191 | 192 | #### ES-256, ES-384 and ES-512 193 | 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. 194 | 195 | ```Go 196 | package main 197 | 198 | import ( 199 | "fmt" 200 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 201 | "github.com/dvsekhvalnov/jose2go" 202 | ) 203 | 204 | func main() { 205 | 206 | payload := `{"hello":"world"}` 207 | 208 | 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}, 209 | []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}, 210 | []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 }) 211 | 212 | token,err := jose.Sign(payload, jose.ES256, privateKey) 213 | 214 | if(err==nil) { 215 | //go use token 216 | fmt.Printf("\ntoken = %v\n",token) 217 | } 218 | } 219 | ``` 220 | 221 | ### Creating encrypted tokens 222 | #### RSA-OAEP-512, RSA-OAEP-384, RSA-OAEP-256, RSA-OAEP and RSA1\_5 key management algorithm 223 | RSA-OAEP-512, RSA-OAEP-384, RSA-OAEP-256, RSA-OAEP and RSA1_5 key management expecting `*rsa.PublicKey` public key of corresponding length. 224 | 225 | ```Go 226 | package main 227 | 228 | import ( 229 | "fmt" 230 | "io/ioutil" 231 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 232 | "github.com/dvsekhvalnov/jose2go" 233 | ) 234 | 235 | func main() { 236 | 237 | payload := `{"hello": "world"}` 238 | 239 | keyBytes,err := ioutil.ReadFile("public.key") 240 | 241 | if(err!=nil) { 242 | panic("invalid key file") 243 | } 244 | 245 | publicKey,e:=Rsa.ReadPublic(keyBytes) 246 | 247 | if(e!=nil) { 248 | panic("invalid key format") 249 | } 250 | 251 | //OR: 252 | //token,err := jose.Encrypt(payload, jose.RSA1_5, jose.A256GCM, publicKey) 253 | token,err := jose.Encrypt(payload, jose.RSA_OAEP, jose.A256GCM, publicKey) 254 | 255 | if(err==nil) { 256 | //go use token 257 | fmt.Printf("\ntoken = %v\n",token) 258 | } 259 | } 260 | ``` 261 | 262 | #### AES Key Wrap key management family of algorithms 263 | AES128KW, AES192KW and AES256KW key management requires `[]byte` array key of corresponding length 264 | 265 | ```Go 266 | package main 267 | 268 | import ( 269 | "fmt" 270 | "github.com/dvsekhvalnov/jose2go" 271 | ) 272 | 273 | func main() { 274 | 275 | payload := `{"hello": "world"}` 276 | 277 | sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} 278 | 279 | token,err := jose.Encrypt(payload,jose.A128KW,jose.A128GCM,sharedKey) 280 | 281 | if(err==nil) { 282 | //go use token 283 | fmt.Printf("\nA128KW A128GCM = %v\n",token) 284 | } 285 | } 286 | ``` 287 | 288 | #### AES GCM Key Wrap key management family of algorithms 289 | AES128GCMKW, AES192GCMKW and AES256GCMKW key management requires `[]byte` array key of corresponding length 290 | 291 | ```Go 292 | package main 293 | 294 | import ( 295 | "fmt" 296 | "github.com/dvsekhvalnov/jose2go" 297 | ) 298 | 299 | func main() { 300 | 301 | payload := `{"hello": "world"}` 302 | 303 | sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} 304 | 305 | token,err := jose.Encrypt(payload,jose.A128GCMKW,jose.A128GCM,sharedKey) 306 | 307 | if(err==nil) { 308 | //go use token 309 | fmt.Printf("\nA128GCMKW A128GCM = %v\n",token) 310 | } 311 | } 312 | ``` 313 | 314 | #### ECDH-ES and ECDH-ES with AES Key Wrap key management family of algorithms 315 | 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: 316 | 317 | ```Go 318 | package main 319 | 320 | import ( 321 | "fmt" 322 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 323 | "github.com/dvsekhvalnov/jose2go" 324 | ) 325 | 326 | func main() { 327 | 328 | payload := `{"hello":"world"}` 329 | 330 | 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}, 331 | []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}) 332 | 333 | token,err := jose.Encrypt(payload, jose.ECDH_ES, jose.A128CBC_HS256, publicKey) 334 | 335 | if(err==nil) { 336 | //go use token 337 | fmt.Printf("\ntoken = %v\n",token) 338 | } 339 | } 340 | ``` 341 | 342 | #### PBES2 using HMAC SHA with AES Key Wrap key management family of algorithms 343 | PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW key management requires `string` passphrase from which actual key will be derived 344 | 345 | ```Go 346 | package main 347 | 348 | import ( 349 | "fmt" 350 | "github.com/dvsekhvalnov/jose2go" 351 | ) 352 | 353 | func main() { 354 | 355 | payload := `{"hello": "world"}` 356 | 357 | passphrase := `top secret` 358 | 359 | token,err := jose.Encrypt(payload,jose.PBES2_HS256_A128KW,jose.A256GCM,passphrase) 360 | 361 | if(err==nil) { 362 | //go use token 363 | fmt.Printf("\nPBES2_HS256_A128KW A256GCM = %v\n",token) 364 | } 365 | } 366 | ``` 367 | 368 | #### DIR direct pre-shared symmetric key management 369 | Direct key management with pre-shared symmetric keys expecting `[]byte` array key of corresponding length: 370 | 371 | ```Go 372 | package main 373 | 374 | import ( 375 | "fmt" 376 | "github.com/dvsekhvalnov/jose2go" 377 | ) 378 | 379 | func main() { 380 | 381 | payload := `{"hello": "world"}` 382 | 383 | sharedKey :=[]byte{194,164,235,6,138,248,171,239,24,216,11,22,137,199,215,133} 384 | 385 | token,err := jose.Encrypt(payload,jose.DIR,jose.A128GCM,sharedKey) 386 | 387 | if(err==nil) { 388 | //go use token 389 | fmt.Printf("\nDIR A128GCM = %v\n",token) 390 | } 391 | } 392 | ``` 393 | 394 | ### Creating compressed & encrypted tokens 395 | #### DEFLATE compression 396 | **jose2go** supports optional DEFLATE compression of payload before encrypting, can be used with all supported encryption and key management algorithms: 397 | 398 | ```Go 399 | package main 400 | 401 | import ( 402 | "fmt" 403 | "github.com/dvsekhvalnov/jose2go" 404 | ) 405 | 406 | func main() { 407 | 408 | payload := `{"hello": "world"}` 409 | 410 | sharedKey := []byte{194, 164, 235, 6, 138, 248, 171, 239, 24, 216, 11, 22, 137, 199, 215, 133} 411 | 412 | token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, sharedKey, jose.Zip(jose.DEF)) 413 | 414 | if err == nil { 415 | //go use token 416 | fmt.Printf("\nDIR A128GCM DEFLATED= %v\n", token) 417 | } 418 | } 419 | ``` 420 | 421 | ### Verifying, Decoding and Decompressing tokens 422 | 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: 423 | 424 | 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. 425 | 426 | **HS256, HS384, HS512** signatures, **A128KW, A192KW, A256KW**,**A128GCMKW, A192GCMKW, A256GCMKW** and **DIR** key management algorithm expecting `[]byte` array key: 427 | 428 | ```Go 429 | package main 430 | 431 | import ( 432 | "fmt" 433 | "github.com/dvsekhvalnov/jose2go" 434 | ) 435 | 436 | func main() { 437 | 438 | token := "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E" 439 | 440 | 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} 441 | 442 | payload, headers, err := jose.Decode(token,sharedKey) 443 | 444 | if(err==nil) { 445 | //go use token 446 | fmt.Printf("\npayload = %v\n",payload) 447 | 448 | //and/or use headers 449 | fmt.Printf("\nheaders = %v\n",headers) 450 | } 451 | } 452 | ``` 453 | 454 | **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: 455 | 456 | ```Go 457 | package main 458 | 459 | import ( 460 | "fmt" 461 | "io/ioutil" 462 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 463 | "github.com/dvsekhvalnov/jose2go" 464 | ) 465 | 466 | func main() { 467 | 468 | token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" 469 | 470 | keyBytes, err := ioutil.ReadFile("public.key") 471 | 472 | if(err!=nil) { 473 | panic("invalid key file") 474 | } 475 | 476 | publicKey, e:=Rsa.ReadPublic(keyBytes) 477 | 478 | if(e!=nil) { 479 | panic("invalid key format") 480 | } 481 | 482 | payload, headers, err := jose.Decode(token, publicKey) 483 | 484 | if(err==nil) { 485 | //go use token 486 | fmt.Printf("\npayload = %v\n",payload) 487 | 488 | //and/or use headers 489 | fmt.Printf("\nheaders = %v\n",headers) 490 | } 491 | } 492 | ``` 493 | 494 | **RSA-OAEP-512**, **RSA-OAEP-384** ,**RSA-OAEP-256**, **RSA-OAEP** and **RSA1_5** key management algorithms expecting `*rsa.PrivateKey` private key of corresponding length: 495 | 496 | ```Go 497 | package main 498 | 499 | import ( 500 | "fmt" 501 | "io/ioutil" 502 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 503 | "github.com/dvsekhvalnov/jose2go" 504 | ) 505 | 506 | func main() { 507 | 508 | token := "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMjU2R0NNIn0.ixD3WVOkvaxeLKi0kyVqTzM6W2EW25SHHYCAr9473Xq528xSK0AVux6kUtv7QMkQKgkMvO8X4VdvonyGkDZTK2jgYUiI06dz7I1sjWJIbyNVrANbBsmBiwikwB-9DLEaKuM85Lwu6gnzbOF6B9R0428ckxmITCPDrzMaXwYZHh46FiSg9djChUTex0pHGhNDiEIgaINpsmqsOFX1L2Y7KM2ZR7wtpR3kidMV3JlxHdKheiPKnDx_eNcdoE-eogPbRGFdkhEE8Dyass1ZSxt4fP27NwsIer5pc0b922_3XWdi1r1TL_fLvGktHLvt6HK6IruXFHpU4x5Z2gTXWxEIog.zzTNmovBowdX2_hi.QSPSgXn0w25ugvzmu2TnhePn.0I3B9BE064HFNP2E0I7M9g" 509 | 510 | keyBytes, err := ioutil.ReadFile("private.key") 511 | 512 | if(err!=nil) { 513 | panic("invalid key file") 514 | } 515 | 516 | privateKey, e:=Rsa.ReadPrivate(keyBytes) 517 | 518 | if(e!=nil) { 519 | panic("invalid key format") 520 | } 521 | 522 | payload, headers, err := jose.Decode(token, privateKey) 523 | 524 | if(err==nil) { 525 | //go use payload 526 | fmt.Printf("\npayload = %v\n",payload) 527 | 528 | //and/or use headers 529 | fmt.Printf("\nheaders = %v\n",headers) 530 | } 531 | } 532 | ``` 533 | 534 | **PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW** key management algorithms expects `string` passpharase as a key 535 | 536 | ```Go 537 | package main 538 | 539 | import ( 540 | "fmt" 541 | "github.com/dvsekhvalnov/jose2go" 542 | ) 543 | 544 | func main() { 545 | 546 | token := `eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJlZWpFZTF0YmJVbU5XV2s2In0.J2HTgltxH3p7A2zDgQWpZPgA2CHTSnDmMhlZWeSOMoZ0YvhphCeg-w.FzYG5AOptknu7jsG.L8jAxfxZhDNIqb0T96YWoznQ.yNeOfQWUbm8KuDGZ_5lL_g` 547 | 548 | passphrase := `top secret` 549 | 550 | payload, headers, err := jose.Decode(token,passphrase) 551 | 552 | if(err==nil) { 553 | //go use token 554 | fmt.Printf("\npayload = %v\n",payload) 555 | 556 | //and/or use headers 557 | fmt.Printf("\nheaders = %v\n",headers) 558 | } 559 | } 560 | ``` 561 | 562 | **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: 563 | 564 | ```Go 565 | package main 566 | 567 | import ( 568 | "fmt" 569 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 570 | "github.com/dvsekhvalnov/jose2go" 571 | ) 572 | 573 | func main() { 574 | 575 | token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" 576 | 577 | 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}, 578 | []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}) 579 | 580 | payload, headers, err := jose.Decode(token, publicKey) 581 | 582 | if(err==nil) { 583 | //go use token 584 | fmt.Printf("\npayload = %v\n",payload) 585 | 586 | //and/or use headers 587 | fmt.Printf("\nheaders = %v\n",headers) 588 | } 589 | } 590 | ``` 591 | 592 | **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: 593 | 594 | ```Go 595 | package main 596 | 597 | import ( 598 | "fmt" 599 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 600 | "github.com/dvsekhvalnov/jose2go" 601 | ) 602 | 603 | func main() { 604 | 605 | token := "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiItVk1LTG5NeW9IVHRGUlpGNnFXNndkRm5BN21KQkdiNzk4V3FVMFV3QVhZIiwieSI6ImhQQWNReTgzVS01Qjl1U21xbnNXcFZzbHVoZGJSZE1nbnZ0cGdmNVhXTjgiLCJjcnYiOiJQLTI1NiJ9fQ..UA3N2j-TbYKKD361AxlXUA.XxFur_nY1GauVp5W_KO2DEHfof5s7kUwvOgghiNNNmnB4Vxj5j8VRS8vMOb51nYy2wqmBb2gBf1IHDcKZdACkCOMqMIcpBvhyqbuKiZPLHiilwSgVV6ubIV88X0vK0C8ZPe5lEyRudbgFjdlTnf8TmsvuAsdtPn9dXwDjUR23bD2ocp8UGAV0lKqKzpAw528vTfD0gwMG8gt_op8yZAxqqLLljMuZdTnjofAfsW2Rq3Z6GyLUlxR51DAUlQKi6UpsKMJoXTrm1Jw8sXBHpsRqA.UHCYOtnqk4SfhAknCnymaQ" 606 | 607 | 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}, 608 | []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}, 609 | []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 }) 610 | 611 | payload, headers, err := jose.Decode(token, privateKey) 612 | 613 | if(err==nil) { 614 | //go use token 615 | fmt.Printf("\npayload = %v\n",payload) 616 | 617 | //and/or use headers 618 | fmt.Printf("\nheaders = %v\n",headers) 619 | } 620 | } 621 | ``` 622 | 623 | ### Adding extra headers 624 | 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. 625 | 626 | Note: **jose2go** do not allow to override `alg`, `enc` and `zip` headers. 627 | 628 | Example of signing with extra headers: 629 | ```Go 630 | token, err := jose.Sign(payload, jose.ES256, key, 631 | jose.Header("keyid", "111-222-333"), 632 | jose.Header("trans-id", "aaa-bbb")) 633 | ``` 634 | 635 | Encryption with extra headers: 636 | ```Go 637 | token, err := jose.Encrypt(payload, jose.DIR, jose.A128GCM, sharedKey, 638 | jose.Headers(map[string]interface{}{"keyid": "111-22-33", "cty": "text/plain"})) 639 | ``` 640 | 641 | ### Two phase validation 642 | 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. 643 | 644 | **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. 645 | 646 | Example of decoding token with callback: 647 | 648 | ```Go 649 | package main 650 | 651 | import ( 652 | "crypto/rsa" 653 | "fmt" 654 | "github.com/dvsekhvalnov/jose2go" 655 | "github.com/dvsekhvalnov/jose2go/keys/rsa" 656 | "io/ioutil" 657 | "errors" 658 | ) 659 | 660 | func main() { 661 | 662 | token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" 663 | 664 | payload, _, err := jose.Decode(token, 665 | func(headers map[string]interface{}, payload string) interface{} { 666 | //log something 667 | fmt.Printf("\nHeaders before decoding: %v\n", headers) 668 | fmt.Printf("\nPayload before decoding: %v\n", payload) 669 | 670 | //lookup key based on keyid header as en example 671 | //or lookup based on something from payload, e.g. 'iss' claim for instance 672 | key := FindKey(headers['keyid']) 673 | 674 | if(key==nil) { 675 | return errors.New("Key not found") 676 | } 677 | 678 | return key; 679 | }) 680 | 681 | if err == nil { 682 | //go use token 683 | fmt.Printf("\ndecoded payload = %v\n", payload) 684 | } 685 | } 686 | ``` 687 | 688 | 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. 689 | 690 | ### Working with binary payload 691 | In addition to work with string payloads (typical use-case) `jose2go` supports 692 | encoding and decoding of raw binary data. `jose.DecodeBytes`, `jose.SignBytes` 693 | and `jose.EncryptBytes` functions provides similar interface but accepting 694 | `[]byte` payloads. 695 | 696 | Examples: 697 | 698 | ```Go 699 | package main 700 | 701 | import ( 702 | "github.com/dvsekhvalnov/jose2go" 703 | ) 704 | 705 | func main() { 706 | 707 | token := `eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJlZWpFZTF0YmJVbU5XV2s2In0.J2HTgltxH3p7A2zDgQWpZPgA2CHTSnDmMhlZWeSOMoZ0YvhphCeg-w.FzYG5AOptknu7jsG.L8jAxfxZhDNIqb0T96YWoznQ.yNeOfQWUbm8KuDGZ_5lL_g` 708 | 709 | passphrase := `top secret` 710 | 711 | payload, headers, err := jose.DecodeBytes(token,passphrase) 712 | 713 | if(err==nil) { 714 | //go use token 715 | //payload = []byte{....} 716 | } 717 | } 718 | ``` 719 | 720 | ```Go 721 | package main 722 | 723 | import ( 724 | "fmt" 725 | "io/ioutil" 726 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 727 | "github.com/dvsekhvalnov/jose2go" 728 | ) 729 | 730 | func main() { 731 | 732 | payload := []byte {0x01, 0x02, 0x03, 0x04} 733 | 734 | keyBytes,err := ioutil.ReadFile("private.key") 735 | 736 | if(err!=nil) { 737 | panic("invalid key file") 738 | } 739 | 740 | privateKey,e:=Rsa.ReadPrivate(keyBytes) 741 | 742 | if(e!=nil) { 743 | panic("invalid key format") 744 | } 745 | 746 | token,err := jose.SignBytes(payload,jose.RS256, privateKey) 747 | 748 | if(err==nil) { 749 | //go use token 750 | fmt.Printf("\nRS256 = %v\n",token) 751 | } 752 | } 753 | ``` 754 | 755 | ```Go 756 | package main 757 | 758 | import ( 759 | "fmt" 760 | "io/ioutil" 761 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 762 | "github.com/dvsekhvalnov/jose2go" 763 | ) 764 | 765 | func main() { 766 | 767 | payload := []byte {0x01, 0x02, 0x03, 0x04} 768 | 769 | keyBytes,err := ioutil.ReadFile("public.key") 770 | 771 | if(err!=nil) { 772 | panic("invalid key file") 773 | } 774 | 775 | publicKey,e:=Rsa.ReadPublic(keyBytes) 776 | 777 | if(e!=nil) { 778 | panic("invalid key format") 779 | } 780 | 781 | token,err := jose.EncryptBytes(payload, jose.RSA_OAEP, jose.A256GCM, publicKey) 782 | 783 | if(err==nil) { 784 | //go use token 785 | fmt.Printf("\ntoken = %v\n",token) 786 | } 787 | } 788 | ``` 789 | ### Dealing with keys 790 | **jose2go** provides several helper methods to simplify loading & importing of elliptic and rsa keys. Import `jose2go/keys/rsa` or `jose2go/keys/ecc` respectively: 791 | 792 | #### RSA keys 793 | 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) 794 | 795 | ```Go 796 | package main 797 | 798 | import ( 799 | "fmt" 800 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 801 | "io/ioutil" 802 | ) 803 | 804 | func main() { 805 | 806 | keyBytes, _ := ioutil.ReadFile("private.key") 807 | 808 | privateKey, err:=Rsa.ReadPrivate(keyBytes) 809 | 810 | if(err!=nil) { 811 | panic("invalid key format") 812 | } 813 | 814 | fmt.Printf("privateKey = %v\n",privateKey) 815 | } 816 | ``` 817 | 818 | 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) 819 | 820 | ```Go 821 | package main 822 | 823 | import ( 824 | "fmt" 825 | Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" 826 | "io/ioutil" 827 | ) 828 | 829 | func main() { 830 | 831 | keyBytes, _ := ioutil.ReadFile("public.cer") 832 | 833 | publicKey, err:=Rsa.ReadPublic(keyBytes) 834 | 835 | if(err!=nil) { 836 | panic("invalid key format") 837 | } 838 | 839 | fmt.Printf("publicKey = %v\n",publicKey) 840 | } 841 | ``` 842 | 843 | #### ECC keys 844 | 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) 845 | 846 | ```Go 847 | package main 848 | 849 | import ( 850 | "fmt" 851 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 852 | "io/ioutil" 853 | ) 854 | 855 | func main() { 856 | 857 | keyBytes, _ := ioutil.ReadFile("ec-private.pem") 858 | 859 | ecPrivKey, err:=ecc.ReadPrivate(keyBytes) 860 | 861 | if(err!=nil) { 862 | panic("invalid key format") 863 | } 864 | 865 | fmt.Printf("ecPrivKey = %v\n",ecPrivKey) 866 | } 867 | ``` 868 | 869 | 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) 870 | 871 | ```Go 872 | package main 873 | 874 | import ( 875 | "fmt" 876 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 877 | "io/ioutil" 878 | ) 879 | 880 | func main() { 881 | 882 | keyBytes, _ := ioutil.ReadFile("ec-public.key") 883 | 884 | ecPubKey, err:=ecc.ReadPublic(keyBytes) 885 | 886 | if(err!=nil) { 887 | panic("invalid key format") 888 | } 889 | 890 | fmt.Printf("ecPubKey = %v\n",ecPubKey) 891 | } 892 | ``` 893 | 894 | 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. 895 | 896 | ```Go 897 | package main 898 | 899 | import ( 900 | "fmt" 901 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 902 | ) 903 | 904 | func main() { 905 | 906 | 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}, 907 | []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}) 908 | 909 | fmt.Printf("ecPubKey = %v\n",ecPubKey) 910 | } 911 | ``` 912 | 913 | 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. 914 | 915 | ```Go 916 | package main 917 | 918 | import ( 919 | "fmt" 920 | "github.com/dvsekhvalnov/jose2go/keys/ecc" 921 | ) 922 | 923 | func main() { 924 | 925 | 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}, 926 | []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}, 927 | []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 }) 928 | 929 | fmt.Printf("ecPrivKey = %v\n",ecPrivKey) 930 | } 931 | ``` 932 | 933 | ### More examples 934 | Checkout `jose_test.go` for more examples. 935 | 936 | ## Customizing library for security 937 | 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. 938 | 939 | ### Deregister algorithm implementations 940 | 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. 941 | 942 | - `func DeregisterJwa(alg string) JwaAlgorithm` 943 | - `func DeregisterJwe(alg string) JweEncryption` 944 | - `func DeregisterJws(alg string) JwsAlgorithm` 945 | - `func DeregisterJwc(alg string) JwcAlgorithm` 946 | 947 | All of them expecting alg name matching `jose` constants and returns implementation that have been deregistered. 948 | 949 | ### Strict validation 950 | Sometimes it is desirable to verify that `alg` or `enc` values are matching expected before attempting to decode actual payload. 951 | `jose2go` provides helper matchers to be used within [Two-phase validation](#two-phase-validation) precheck: 952 | 953 | - `jose.Alg(key, alg)` - to match alg header 954 | - `jose.Enc(key, alg)` - to match alg and enc headers 955 | 956 | ```Go 957 | token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" 958 | 959 | key := Rsa.ReadPublic(....) 960 | 961 | // we expecting 'RS256' alg here and if matching continue to decode with a key 962 | payload, header, err := jose.Decode(token, Alg(key, "RS256")) 963 | 964 | // or match both alg and enc for decrypting scenarios 965 | payload, header, err := jose.Decode(token, Enc(key, "RSA-OAEP-256", "A192CBC-HS384")) 966 | ``` 967 | 968 | ### Customizing PBKDF2 969 | 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. 970 | 971 | By default, maxIterations is set according to [OWASP PBKDF2](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2) Recomendations: 972 | 973 | ``` 974 | PBES2-HS256+A128KW: 1300000 975 | PBES2-HS384+A192KW: 950000 976 | PBES2-HS512+A256KW: 600000 977 | ``` 978 | 979 | , while minIterations kept at 0 for backward compatibility. 980 | 981 | If it is desired to implement different limits, register new implementation with new parameters: 982 | 983 | ```Go 984 | jose.RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 1300000)) 985 | jose.RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 950000)) 986 | jose.RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 600000)) 987 | ``` 988 | 989 | 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: 990 | 991 | ```Go 992 | test, headers, err := Decode(token, func(headers map[string]interface{}, payload string) interface{} { 993 | alg := headers["alg"].(string) 994 | p2c := headers["p2c"].(float64) 995 | 996 | if strings.HasPrefix(alg, "PBES2-") && int64(p2c) > 100 { 997 | return errors.New("Too many p2c interation count, aborting") 998 | } 999 | 1000 | return "top secret" 1001 | }) 1002 | ``` 1003 | 1004 | ### Customizing compression 1005 | There were denial-of-service attacks reported on JWT libraries that supports deflate compression by constructing malicious payload that explodes in terms of RAM on decompression. See for details: [#33](https://github.com/dvsekhvalnov/jose2go/issues/33) 1006 | 1007 | As of v1.7.0 `jose2go` limits decompression buffer to 250Kb to limit memory consumption and additionaly provides a way to adjust the limit according to specific scenarios: 1008 | 1009 | ```Go 1010 | // Override compression alg with new limits (10Kb example) 1011 | jose.RegisterJwc(RegisterJwc(NewDeflate(10240))) 1012 | ``` 1013 | 1014 | ## Changelog 1015 | ### 1.8 1016 | - RSA-OAEP-384 and RSA-OAEP-512 key management algorithms 1017 | 1018 | ### 1.7 1019 | - 250Kb limit on decompression buffer 1020 | - ability to register deflate compressor with custom limits 1021 | 1022 | ### 1.6 1023 | - ability to deregister specific algorithms 1024 | - configurable min/max restrictions for PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW 1025 | 1026 | ### 1.5 1027 | - security and bug fixes 1028 | 1029 | ### 1.4 1030 | - removed extra headers to be inserted by library 1031 | 1032 | ### 1.3 1033 | - security fixes: Invalid Curve Attack on NIST curves 1034 | 1035 | ### 1.2 1036 | - interface to access token headers after decoding 1037 | - interface to provide extra headers for token encoding 1038 | - two-phase validation support 1039 | 1040 | ### 1.1 1041 | - security and bug fixes 1042 | 1043 | ### 1.0 1044 | - initial stable version with full suite JOSE spec support 1045 | --------------------------------------------------------------------------------