func NewRawKeyFromPublicKey(pubkey *ecdsa.PublicKey) *rawkey { r := &rawkey{} r.Curve = curve{pubkey.Curve} r.X = buffer.Buffer(pubkey.X.Bytes()) r.Y = buffer.Buffer(pubkey.Y.Bytes()) return r }
// Serialize converts the mssage into a compact JSON format func (s CompactSerialize) Serialize(m *Message) ([]byte, error) { if len(m.Signatures) != 1 { return nil, errors.New("wrong number of signatures for compact serialization") } signature := m.Signatures[0] hdr := NewHeader() if err := hdr.Copy(signature.ProtectedHeader.Header); err != nil { return nil, err } hdr, err := hdr.Merge(signature.PublicHeader) if err != nil { return nil, err } hdrbuf, err := hdr.Base64Encode() if err != nil { return nil, err } b64payload, err := m.Payload.Base64Encode() if err != nil { return nil, err } b64signature, err := buffer.Buffer(signature.Signature).Base64Encode() if err != nil { return nil, err } buf := append(append(append(append(hdrbuf, '.'), b64payload...), '.'), b64signature...) return buf, nil }
// NewRsaPublicKey creates a new JWK using the given key func NewRsaPublicKey(pk *rsa.PublicKey) (*RsaPublicKey, error) { k := &RsaPublicKey{ EssentialHeader: &EssentialHeader{KeyType: "RSA"}, N: buffer.Buffer(pk.N.Bytes()), E: buffer.FromUint(uint64(pk.E)), } return k, nil }
func (h Header) Base64Encode() ([]byte, error) { b, err := json.Marshal(h) if err != nil { return nil, err } return buffer.Buffer(b).Base64Encode() }
// NewRsaPrivateKey creates a new JWK using the given key func NewRsaPrivateKey(pk *rsa.PrivateKey) (*RsaPrivateKey, error) { if len(pk.Primes) < 2 { return nil, errors.New("two primes required for RSA private key") } pub, err := NewRsaPublicKey(&pk.PublicKey) if err != nil { return nil, err } k := &RsaPrivateKey{ RsaPublicKey: pub, D: buffer.Buffer(pk.D.Bytes()), P: buffer.Buffer(pk.Primes[0].Bytes()), Q: buffer.Buffer(pk.Primes[1].Bytes()), } return k, nil }
func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF { algbuf := buffer.Buffer(alg).NData() apubuf := buffer.Buffer(apu).NData() apvbuf := buffer.Buffer(apv).NData() concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo)) n := copy(concat, algbuf) n += copy(concat[n:], apubuf) n += copy(concat[n:], apvbuf) n += copy(concat[n:], pubinfo) n += copy(concat[n:], privinfo) return &KDF{ hash: hash.New(), otherinfo: concat, round: 1, z: Z, } }
func (e EncodedHeader) MarshalJSON() ([]byte, error) { buf, err := json.Marshal(e.Header) if err != nil { return nil, err } buf, err = buffer.Buffer(buf).Base64Encode() if err != nil { return nil, err } return json.Marshal(string(buf)) }
// Base64Encode creates the base64 encoded version of the JSON // representation of this header func (e EncodedHeader) Base64Encode() ([]byte, error) { buf, err := json.Marshal(e.Header) if err != nil { return nil, err } buf, err = buffer.Buffer(buf).Base64Encode() if err != nil { return nil, err } return buf, nil }
func NewRawKeyFromPrivateKey(privkey *rsa.PrivateKey) *rawkey { r := NewRawKeyFromPublicKey(&privkey.PublicKey) r.D = buffer.Buffer(privkey.D.Bytes()) r.P = buffer.Buffer(privkey.Primes[0].Bytes()) r.Q = buffer.Buffer(privkey.Primes[1].Bytes()) r.Dp = buffer.Buffer(privkey.Precomputed.Dp.Bytes()) r.Dq = buffer.Buffer(privkey.Precomputed.Dq.Bytes()) r.Qi = buffer.Buffer(privkey.Precomputed.Qinv.Bytes()) return r }
// parseCompact parses a JWS value serialized via compact serialization. func parseCompact(buf []byte) (*Message, error) { parts := bytes.Split(buf, []byte{'.'}) if len(parts) != 3 { return nil, ErrInvalidCompactPartsCount } enc := base64.RawURLEncoding hdrbuf, err := buffer.FromBase64(parts[0]) if err != nil { return nil, err } debug.Printf("hdrbuf = %s", hdrbuf.Bytes()) hdr := &EncodedHeader{Header: NewHeader()} if err := json.Unmarshal(hdrbuf.Bytes(), hdr.Header); err != nil { return nil, err } hdr.Source = hdrbuf payload, err := buffer.FromBase64(parts[1]) if err != nil { return nil, err } signature := make([]byte, enc.DecodedLen(len(parts[2]))) if _, err := enc.Decode(signature, parts[2]); err != nil { return nil, err } signature = bytes.TrimRight(signature, "\x00") s := NewSignature() s.Signature = signature s.ProtectedHeader = hdr m := &Message{ Payload: buffer.Buffer(payload), Signatures: []Signature{*s}, } return m, nil }
func TestMultiSigner(t *testing.T) { rsakey, err := rsa.GenerateKey(rand.Reader, 2048) if !assert.NoError(t, err, "RSA key generated") { return } dsakey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if !assert.NoError(t, err, "ECDSA key generated") { return } ms := &MultiSign{} s1, err := NewRsaSign(jwa.RS256, rsakey) if !assert.NoError(t, err, "RSA Signer created") { return } s1.PublicHeaders().Set("kid", "2010-12-29") ms.AddSigner(s1) s2, err := NewEcdsaSign(jwa.ES256, dsakey) if !assert.NoError(t, err, "DSA Signer created") { return } s2.PublicHeaders().Set("kid", "e9bc097a-ce51-4036-9562-d2ade882db0d") ms.AddSigner(s2) v := strings.Join([]string{`{"iss":"joe",`, ` "exp":1300819380,`, ` "http://example.com/is_root":true}`}, "\r\n") m, err := ms.Sign(buffer.Buffer(v)) if !assert.NoError(t, err, "MultiSign succeeded") { return } jsonbuf, _ := json.MarshalIndent(m, "", " ") t.Logf("%s", jsonbuf) }
// Set sets the value of the given key to the given value. If it's // one of the known keys, it will be set in EssentialHeader field. // Otherwise, it is set in PrivateParams field. func (h *Header) Set(key string, value interface{}) error { switch key { case "alg": var v jwa.KeyEncryptionAlgorithm s, ok := value.(string) if ok { v = jwa.KeyEncryptionAlgorithm(s) } else { v, ok = value.(jwa.KeyEncryptionAlgorithm) if !ok { return ErrInvalidHeaderValue } } h.Algorithm = v case "apu": var v buffer.Buffer switch value.(type) { case buffer.Buffer: v = value.(buffer.Buffer) case []byte: v = buffer.Buffer(value.([]byte)) case string: v = buffer.Buffer(value.(string)) default: return ErrInvalidHeaderValue } h.AgreementPartyUInfo = v case "apv": var v buffer.Buffer switch value.(type) { case buffer.Buffer: v = value.(buffer.Buffer) case []byte: v = buffer.Buffer(value.([]byte)) case string: v = buffer.Buffer(value.(string)) default: return ErrInvalidHeaderValue } h.AgreementPartyVInfo = v case "enc": var v jwa.ContentEncryptionAlgorithm s, ok := value.(string) if ok { v = jwa.ContentEncryptionAlgorithm(s) } else { v, ok = value.(jwa.ContentEncryptionAlgorithm) if !ok { return ErrInvalidHeaderValue } } h.ContentEncryption = v case "cty": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } h.ContentType = v case "epk": v, ok := value.(*jwk.EcdsaPublicKey) if !ok { return ErrInvalidHeaderValue } h.EphemeralPublicKey = v case "kid": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } h.KeyID = v case "typ": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } h.Type = v case "x5t": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } h.X509CertThumbprint = v case "x5t#256": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } h.X509CertThumbprintS256 = v case "x5c": v, ok := value.([]string) if !ok { return ErrInvalidHeaderValue } h.X509CertChain = v case "crit": v, ok := value.([]string) if !ok { return ErrInvalidHeaderValue } h.Critical = v case "jku": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } u, err := url.Parse(v) if err != nil { return ErrInvalidHeaderValue } h.JwkSetURL = u case "x5u": v, ok := value.(string) if !ok { return ErrInvalidHeaderValue } u, err := url.Parse(v) if err != nil { return ErrInvalidHeaderValue } h.X509Url = u default: h.PrivateParams[key] = value } return nil }
func NewRawKeyFromPublicKey(pubkey *rsa.PublicKey) *rawkey { r := &rawkey{} r.N = buffer.Buffer(pubkey.N.Bytes()) r.E = buffer.FromUint(uint64(pubkey.E)) return r }
// Sign takes a payload, and creates a JWS signed message. func (m *MultiSign) Sign(payload []byte) (*Message, error) { encoded, err := buffer.Buffer(payload).Base64Encode() if err != nil { return nil, err } msg := &Message{ Payload: buffer.Buffer(payload), Signatures: []Signature{}, } for _, signer := range m.Signers { protected, err := NewHeader().Merge(signer.PublicHeaders()) if err != nil { return nil, err } protected, err = protected.Merge(signer.ProtectedHeaders()) if err != nil { return nil, err } protected.Algorithm = signer.SignatureAlgorithm() protbuf, err := protected.Base64Encode() if err != nil { return nil, err } siv := append(append(protbuf, '.'), encoded...) sigbuf, err := signer.PayloadSign(siv) if err != nil { return nil, err } hdr, err := NewHeader().Merge(signer.PublicHeaders()) if err != nil { return nil, err } /* if hdr.KeyID == "" { if protected.KeyID != "" { // Use the JWK in the protected field... hdr.KeyID = protected.KeyID } else if signer.Kid() != "" { // Or, get it from the signer hdr.KeyID = signer.Kid() } } */ sig := Signature{ PublicHeader: hdr, ProtectedHeader: &EncodedHeader{Header: protected}, Signature: buffer.Buffer(sigbuf), } msg.Signatures = append(msg.Signatures, sig) } return msg, nil }
// TestEncode_HS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.1 works func TestEncode_HS256Compact(t *testing.T) { const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"HS256"}` const hmacKey = `AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow` const expected = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` hmacKeyDecoded := buffer.Buffer{} hmacKeyDecoded.Base64Decode([]byte(hmacKey)) sign, err := NewHmacSign(jwa.HS256, hmacKeyDecoded.Bytes()) if !assert.NoError(t, err, "HmacSign created successfully") { return } hdrbuf, err := buffer.Buffer(hdr).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } payload, err := buffer.Buffer(examplePayload).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } signingInput := bytes.Join( [][]byte{ hdrbuf, payload, }, []byte{'.'}, ) signature, err := sign.PayloadSign(signingInput) if !assert.NoError(t, err, "PayloadSign is successful") { return } sigbuf, err := buffer.Buffer(signature).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } encoded := bytes.Join( [][]byte{ signingInput, sigbuf, }, []byte{'.'}, ) if !assert.Equal(t, expected, string(encoded), "generated compact serialization should match") { return } msg, err := Parse(encoded) if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") { return } hdrs := msg.Signatures[0].MergedHeaders() if !assert.Equal(t, hdrs.Algorithm(), jwa.HS256, "Algorithm in header matches") { return } v, err := NewHmacVerify(jwa.HS256, hmacKeyDecoded.Bytes()) if !assert.NoError(t, err, "HmacVerify created") { return } if !assert.NoError(t, v.Verify(msg), "Verify succeeds") { return } }
// TestEncode_RS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.2 works func TestEncode_RS256Compact(t *testing.T) { const hdr = `{"alg":"RS256"}` const expected = `eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw` const jwksrc = `{ "kty":"RSA", "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", "e":"AQAB", "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" }` privkey, err := rsautil.PrivateKeyFromJSON([]byte(jwksrc)) if !assert.NoError(t, err, "parsing jwk should be successful") { return } sign, err := NewRsaSign(jwa.RS256, privkey) if !assert.NoError(t, err, "RsaSign created successfully") { return } hdrbuf, err := buffer.Buffer(hdr).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } payload, err := buffer.Buffer(examplePayload).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } signingInput := bytes.Join( [][]byte{ hdrbuf, payload, }, []byte{'.'}, ) signature, err := sign.PayloadSign(signingInput) if !assert.NoError(t, err, "PayloadSign is successful") { return } sigbuf, err := buffer.Buffer(signature).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } encoded := bytes.Join( [][]byte{ signingInput, sigbuf, }, []byte{'.'}, ) if !assert.Equal(t, expected, string(encoded), "generated compact serialization should match") { return } msg, err := Parse(encoded) if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") { return } hdrs := msg.Signatures[0].MergedHeaders() if !assert.Equal(t, hdrs.Algorithm(), jwa.RS256, "Algorithm in header matches") { return } v, err := NewRsaVerify(jwa.RS256, &privkey.PublicKey) if !assert.NoError(t, err, "Verify created") { return } if !assert.NoError(t, v.Verify(msg), "Verify succeeds") { return } }
func NewRawKeyFromPrivateKey(privkey *ecdsa.PrivateKey) *rawkey { r := NewRawKeyFromPublicKey(&privkey.PublicKey) r.D = buffer.Buffer(privkey.D.Bytes()) return r }
// TestEncode_ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works func TestEncode_ES256Compact(t *testing.T) { const hdr = `{"alg":"ES256"}` const jwksrc = `{ "kty":"EC", "crv":"P-256", "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" }` privkey, err := ecdsautil.PrivateKeyFromJSON([]byte(jwksrc)) if !assert.NoError(t, err, "parsing jwk should be successful") { return } sign, err := NewEcdsaSign(jwa.ES256, privkey) if !assert.NoError(t, err, "RsaSign created successfully") { return } hdrbuf, err := buffer.Buffer(hdr).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } payload, err := buffer.Buffer(examplePayload).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } signingInput := bytes.Join( [][]byte{ hdrbuf, payload, }, []byte{'.'}, ) signature, err := sign.PayloadSign(signingInput) if !assert.NoError(t, err, "PayloadSign is successful") { return } sigbuf, err := buffer.Buffer(signature).Base64Encode() if !assert.NoError(t, err, "base64 encode successful") { return } encoded := bytes.Join( [][]byte{ signingInput, sigbuf, }, []byte{'.'}, ) // The signature contains random factor, so unfortunately we can't match // the output against a fixed expected outcome. We'll wave doing an // exact match, and just try to verify using the signature msg, err := Parse(encoded) if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") { return } hdrs := msg.Signatures[0].MergedHeaders() if !assert.Equal(t, hdrs.Algorithm(), jwa.ES256, "Algorithm in header matches") { return } v, err := NewEcdsaVerify(jwa.ES256, &privkey.PublicKey) if !assert.NoError(t, err, "EcdsaVerify created") { return } if !assert.NoError(t, v.Verify(msg), "Verify succeeds") { return } }