func TestJWKSymmetricKey(t *testing.T) { sample1 := `{"kty":"oct","alg":"A128KW","k":"GawgguFyGrWKav7AX4VKUg"}` sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}` var jwk1 JsonWebKey json.Unmarshal([]byte(sample1), &jwk1) if jwk1.Algorithm != "A128KW" { t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm) } expected1 := fromHexBytes("19ac2082e1721ab58a6afec05f854a52") if !bytes.Equal(jwk1.Key.([]byte), expected1) { t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected1), hex.EncodeToString(jwk1.Key.([]byte))) } var jwk2 JsonWebKey json.Unmarshal([]byte(sample2), &jwk2) if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" { t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID) } expected2 := fromHexBytes(` 0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebf d3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3`) if !bytes.Equal(jwk2.Key.([]byte), expected2) { t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected2), hex.EncodeToString(jwk2.Key.([]byte))) } }
func TestMarshalNonPointer(t *testing.T) { type EmbedsKey struct { Key JsonWebKey } keyJson := []byte(`{ "e": "AQAB", "kty": "RSA", "n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw" }`) var parsedKey JsonWebKey err := json.Unmarshal(keyJson, &parsedKey) if err != nil { t.Error(fmt.Sprintf("Error unmarshalling key: %v", err)) return } ek := EmbedsKey{ Key: parsedKey, } out, err := json.Marshal(ek) if err != nil { t.Error(fmt.Sprintf("Error marshalling JSON: %v", err)) return } expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}" if string(out) != expected { t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out)) } }
// parseEncryptedFull parses a message in compact format. func parseEncryptedFull(input string) (*JsonWebEncryption, error) { var parsed rawJsonWebEncryption err := json.Unmarshal([]byte(input), &parsed) if err != nil { return nil, err } return parsed.sanitized() }
// parseSignedFull parses a message in full format. func parseSignedFull(input string) (*JsonWebSignature, error) { var parsed rawJsonWebSignature err := json.Unmarshal([]byte(input), &parsed) if err != nil { return nil, err } return parsed.sanitized() }
func TestSignerKid(t *testing.T) { kid := "DEADBEEF" payload := []byte("Lorem ipsum dolor sit amet") key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Error("problem generating test signing key", err) } basejwk := JsonWebKey{Key: key} jsonbar, err := basejwk.MarshalJSON() if err != nil { t.Error("problem marshalling base JWK", err) } var jsonmsi map[string]interface{} err = json.Unmarshal(jsonbar, &jsonmsi) if err != nil { t.Error("problem unmarshalling base JWK", err) } jsonmsi["kid"] = kid jsonbar2, err := json.Marshal(jsonmsi) if err != nil { t.Error("problem marshalling kided JWK", err) } var jwk JsonWebKey err = jwk.UnmarshalJSON(jsonbar2) if err != nil { t.Error("problem unmarshalling kided JWK", err) } signer, err := NewSigner(ES256, &jwk) if err != nil { t.Error("problem creating signer", err) } signed, err := signer.Sign(payload) serialized := signed.FullSerialize() parsed, err := ParseSigned(serialized) if err != nil { t.Error("problem parsing signed object", err) } if parsed.Signatures[0].Header.KeyID != kid { t.Error("KeyID did not survive trip") } }
// UnmarshalJSON reads a key from its JSON representation. func (k *JsonWebKey) UnmarshalJSON(data []byte) (err error) { var raw rawJsonWebKey err = json.Unmarshal(data, &raw) if err != nil { return err } var key interface{} switch raw.Kty { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() } else { key, err = raw.ecPublicKey() } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() } else { key, err = raw.rsaPublicKey() } case "oct": key, err = raw.symmetricKey() default: err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) } if err == nil { *k = JsonWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} } k.Certificates = make([]*x509.Certificate, len(raw.X5c)) for i, cert := range raw.X5c { raw, err := base64.StdEncoding.DecodeString(cert) if err != nil { return err } k.Certificates[i], err = x509.ParseCertificate(raw) if err != nil { return err } } return }
func (b *byteBuffer) UnmarshalJSON(data []byte) error { var encoded string err := json.Unmarshal(data, &encoded) if err != nil { return err } if encoded == "" { return nil } decoded, err := base64URLDecode(encoded) if err != nil { return err } *b = *newBuffer(decoded) return nil }
func TestMarshalUnmarshalJWKSet(t *testing.T) { jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"} jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"} var set JsonWebKeySet set.Keys = append(set.Keys, jwk1) set.Keys = append(set.Keys, jwk2) jsonbar, err := json.Marshal(&set) if err != nil { t.Error("problem marshalling set", err) } var set2 JsonWebKeySet err = json.Unmarshal(jsonbar, &set2) if err != nil { t.Error("problem unmarshalling set", err) } jsonbar2, err := json.Marshal(&set2) if err != nil { t.Error("problem marshalling set", err) } if !bytes.Equal(jsonbar, jsonbar2) { t.Error("roundtrip should not lose information") } }
// sanitized produces a cleaned-up JWE object from the raw JSON. func (parsed *rawJsonWebEncryption) sanitized() (*JsonWebEncryption, error) { obj := &JsonWebEncryption{ original: parsed, unprotected: parsed.Unprotected, } // Check that there is not a nonce in the unprotected headers if (parsed.Unprotected != nil && parsed.Unprotected.Nonce != "") || (parsed.Header != nil && parsed.Header.Nonce != "") { return nil, ErrUnprotectedNonce } if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected) if err != nil { return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64()) } } // Note: this must be called _after_ we parse the protected header, // otherwise fields from the protected header will not get picked up. obj.Header = obj.mergedHeaders(nil).sanitized() if len(parsed.Recipients) == 0 { obj.recipients = []recipientInfo{ recipientInfo{ header: parsed.Header, encryptedKey: parsed.EncryptedKey.bytes(), }, } } else { obj.recipients = make([]recipientInfo, len(parsed.Recipients)) for r := range parsed.Recipients { encryptedKey, err := base64URLDecode(parsed.Recipients[r].EncryptedKey) if err != nil { return nil, err } // Check that there is not a nonce in the unprotected header if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.Nonce != "" { return nil, ErrUnprotectedNonce } obj.recipients[r].header = parsed.Recipients[r].Header obj.recipients[r].encryptedKey = encryptedKey } } for _, recipient := range obj.recipients { headers := obj.mergedHeaders(&recipient) if headers.Alg == "" || headers.Enc == "" { return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers") } } obj.iv = parsed.Iv.bytes() obj.ciphertext = parsed.Ciphertext.bytes() obj.tag = parsed.Tag.bytes() obj.aad = parsed.Aad.bytes() return obj, nil }
func UnmarshalJSON(data []byte, v interface{}) error { return json.Unmarshal(data, v) }
// sanitized produces a cleaned-up JWS object from the raw JSON. func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) { if parsed.Payload == nil { return nil, fmt.Errorf("square/go-jose: missing payload in JWS message") } obj := &JsonWebSignature{ payload: parsed.Payload.bytes(), Signatures: make([]Signature, len(parsed.Signatures)), } if len(parsed.Signatures) == 0 { // No signatures array, must be flattened serialization signature := Signature{} if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { signature.protected = &rawHeader{} err := json.Unmarshal(parsed.Protected.bytes(), signature.protected) if err != nil { return nil, err } } // Check that there is not a nonce in the unprotected header if parsed.Header != nil && parsed.Header.Nonce != "" { return nil, ErrUnprotectedNonce } signature.header = parsed.Header signature.Signature = parsed.Signature.bytes() // Make a fake "original" rawSignatureInfo to store the unprocessed // Protected header. This is necessary because the Protected header can // contain arbitrary fields not registered as part of the spec. See // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4 // If we unmarshal Protected into a rawHeader with its explicit list of fields, // we cannot marshal losslessly. So we have to keep around the original bytes. // This is used in computeAuthData, which will first attempt to use // the original bytes of a protected header, and fall back on marshaling the // header struct only if those bytes are not available. signature.original = &rawSignatureInfo{ Protected: parsed.Protected, Header: parsed.Header, Signature: parsed.Signature, } signature.Header = signature.mergedHeaders().sanitized() // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded. jwk := signature.Header.JsonWebKey if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") } obj.Signatures = append(obj.Signatures, signature) } for i, sig := range parsed.Signatures { if sig.Protected != nil && len(sig.Protected.bytes()) > 0 { obj.Signatures[i].protected = &rawHeader{} err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected) if err != nil { return nil, err } } // Check that there is not a nonce in the unprotected header if sig.Header != nil && sig.Header.Nonce != "" { return nil, ErrUnprotectedNonce } obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized() obj.Signatures[i].Signature = sig.Signature.bytes() // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded. jwk := obj.Signatures[i].Header.JsonWebKey if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") } // Copy value of sig original := sig obj.Signatures[i].header = sig.Header obj.Signatures[i].original = &original } return obj, nil }