658 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			658 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
package libtrust
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/x509"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"time"
 | 
						|
	"unicode"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// ErrInvalidSignContent is used when the content to be signed is invalid.
 | 
						|
	ErrInvalidSignContent = errors.New("invalid sign content")
 | 
						|
 | 
						|
	// ErrInvalidJSONContent is used when invalid json is encountered.
 | 
						|
	ErrInvalidJSONContent = errors.New("invalid json content")
 | 
						|
 | 
						|
	// ErrMissingSignatureKey is used when the specified signature key
 | 
						|
	// does not exist in the JSON content.
 | 
						|
	ErrMissingSignatureKey = errors.New("missing signature key")
 | 
						|
)
 | 
						|
 | 
						|
type jsHeader struct {
 | 
						|
	JWK       PublicKey `json:"jwk,omitempty"`
 | 
						|
	Algorithm string    `json:"alg"`
 | 
						|
	Chain     []string  `json:"x5c,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
type jsSignature struct {
 | 
						|
	Header    jsHeader `json:"header"`
 | 
						|
	Signature string   `json:"signature"`
 | 
						|
	Protected string   `json:"protected,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
type jsSignaturesSorted []jsSignature
 | 
						|
 | 
						|
func (jsbkid jsSignaturesSorted) Swap(i, j int) { jsbkid[i], jsbkid[j] = jsbkid[j], jsbkid[i] }
 | 
						|
func (jsbkid jsSignaturesSorted) Len() int      { return len(jsbkid) }
 | 
						|
 | 
						|
func (jsbkid jsSignaturesSorted) Less(i, j int) bool {
 | 
						|
	ki, kj := jsbkid[i].Header.JWK.KeyID(), jsbkid[j].Header.JWK.KeyID()
 | 
						|
	si, sj := jsbkid[i].Signature, jsbkid[j].Signature
 | 
						|
 | 
						|
	if ki == kj {
 | 
						|
		return si < sj
 | 
						|
	}
 | 
						|
 | 
						|
	return ki < kj
 | 
						|
}
 | 
						|
 | 
						|
type signKey struct {
 | 
						|
	PrivateKey
 | 
						|
	Chain []*x509.Certificate
 | 
						|
}
 | 
						|
 | 
						|
// JSONSignature represents a signature of a json object.
 | 
						|
type JSONSignature struct {
 | 
						|
	payload      string
 | 
						|
	signatures   []jsSignature
 | 
						|
	indent       string
 | 
						|
	formatLength int
 | 
						|
	formatTail   []byte
 | 
						|
}
 | 
						|
 | 
						|
func newJSONSignature() *JSONSignature {
 | 
						|
	return &JSONSignature{
 | 
						|
		signatures: make([]jsSignature, 0, 1),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Payload returns the encoded payload of the signature. This
 | 
						|
// payload should not be signed directly
 | 
						|
func (js *JSONSignature) Payload() ([]byte, error) {
 | 
						|
	return joseBase64UrlDecode(js.payload)
 | 
						|
}
 | 
						|
 | 
						|
func (js *JSONSignature) protectedHeader() (string, error) {
 | 
						|
	protected := map[string]interface{}{
 | 
						|
		"formatLength": js.formatLength,
 | 
						|
		"formatTail":   joseBase64UrlEncode(js.formatTail),
 | 
						|
		"time":         time.Now().UTC().Format(time.RFC3339),
 | 
						|
	}
 | 
						|
	protectedBytes, err := json.Marshal(protected)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	return joseBase64UrlEncode(protectedBytes), nil
 | 
						|
}
 | 
						|
 | 
						|
func (js *JSONSignature) signBytes(protectedHeader string) ([]byte, error) {
 | 
						|
	buf := make([]byte, len(js.payload)+len(protectedHeader)+1)
 | 
						|
	copy(buf, protectedHeader)
 | 
						|
	buf[len(protectedHeader)] = '.'
 | 
						|
	copy(buf[len(protectedHeader)+1:], js.payload)
 | 
						|
	return buf, nil
 | 
						|
}
 | 
						|
 | 
						|
// Sign adds a signature using the given private key.
 | 
						|
func (js *JSONSignature) Sign(key PrivateKey) error {
 | 
						|
	protected, err := js.protectedHeader()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	signBytes, err := js.signBytes(protected)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	js.signatures = append(js.signatures, jsSignature{
 | 
						|
		Header: jsHeader{
 | 
						|
			JWK:       key.PublicKey(),
 | 
						|
			Algorithm: algorithm,
 | 
						|
		},
 | 
						|
		Signature: joseBase64UrlEncode(sigBytes),
 | 
						|
		Protected: protected,
 | 
						|
	})
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// SignWithChain adds a signature using the given private key
 | 
						|
// and setting the x509 chain. The public key of the first element
 | 
						|
// in the chain must be the public key corresponding with the sign key.
 | 
						|
func (js *JSONSignature) SignWithChain(key PrivateKey, chain []*x509.Certificate) error {
 | 
						|
	// Ensure key.Chain[0] is public key for key
 | 
						|
	//key.Chain.PublicKey
 | 
						|
	//key.PublicKey().CryptoPublicKey()
 | 
						|
 | 
						|
	// Verify chain
 | 
						|
	protected, err := js.protectedHeader()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	signBytes, err := js.signBytes(protected)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	header := jsHeader{
 | 
						|
		Chain:     make([]string, len(chain)),
 | 
						|
		Algorithm: algorithm,
 | 
						|
	}
 | 
						|
 | 
						|
	for i, cert := range chain {
 | 
						|
		header.Chain[i] = base64.StdEncoding.EncodeToString(cert.Raw)
 | 
						|
	}
 | 
						|
 | 
						|
	js.signatures = append(js.signatures, jsSignature{
 | 
						|
		Header:    header,
 | 
						|
		Signature: joseBase64UrlEncode(sigBytes),
 | 
						|
		Protected: protected,
 | 
						|
	})
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Verify verifies all the signatures and returns the list of
 | 
						|
// public keys used to sign. Any x509 chains are not checked.
 | 
						|
func (js *JSONSignature) Verify() ([]PublicKey, error) {
 | 
						|
	keys := make([]PublicKey, len(js.signatures))
 | 
						|
	for i, signature := range js.signatures {
 | 
						|
		signBytes, err := js.signBytes(signature.Protected)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		var publicKey PublicKey
 | 
						|
		if len(signature.Header.Chain) > 0 {
 | 
						|
			certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			cert, err := x509.ParseCertificate(certBytes)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			publicKey, err = FromCryptoPublicKey(cert.PublicKey)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		} else if signature.Header.JWK != nil {
 | 
						|
			publicKey = signature.Header.JWK
 | 
						|
		} else {
 | 
						|
			return nil, errors.New("missing public key")
 | 
						|
		}
 | 
						|
 | 
						|
		sigBytes, err := joseBase64UrlDecode(signature.Signature)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		keys[i] = publicKey
 | 
						|
	}
 | 
						|
	return keys, nil
 | 
						|
}
 | 
						|
 | 
						|
// VerifyChains verifies all the signatures and the chains associated
 | 
						|
// with each signature and returns the list of verified chains.
 | 
						|
// Signatures without an x509 chain are not checked.
 | 
						|
func (js *JSONSignature) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) {
 | 
						|
	chains := make([][]*x509.Certificate, 0, len(js.signatures))
 | 
						|
	for _, signature := range js.signatures {
 | 
						|
		signBytes, err := js.signBytes(signature.Protected)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		var publicKey PublicKey
 | 
						|
		if len(signature.Header.Chain) > 0 {
 | 
						|
			certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			cert, err := x509.ParseCertificate(certBytes)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			publicKey, err = FromCryptoPublicKey(cert.PublicKey)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			intermediates := x509.NewCertPool()
 | 
						|
			if len(signature.Header.Chain) > 1 {
 | 
						|
				intermediateChain := signature.Header.Chain[1:]
 | 
						|
				for i := range intermediateChain {
 | 
						|
					certBytes, err := base64.StdEncoding.DecodeString(intermediateChain[i])
 | 
						|
					if err != nil {
 | 
						|
						return nil, err
 | 
						|
					}
 | 
						|
					intermediate, err := x509.ParseCertificate(certBytes)
 | 
						|
					if err != nil {
 | 
						|
						return nil, err
 | 
						|
					}
 | 
						|
					intermediates.AddCert(intermediate)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			verifyOptions := x509.VerifyOptions{
 | 
						|
				Intermediates: intermediates,
 | 
						|
				Roots:         ca,
 | 
						|
			}
 | 
						|
 | 
						|
			verifiedChains, err := cert.Verify(verifyOptions)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			chains = append(chains, verifiedChains...)
 | 
						|
 | 
						|
			sigBytes, err := joseBase64UrlDecode(signature.Signature)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
 | 
						|
			err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
	return chains, nil
 | 
						|
}
 | 
						|
 | 
						|
// JWS returns JSON serialized JWS according to
 | 
						|
// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-7.2
 | 
						|
func (js *JSONSignature) JWS() ([]byte, error) {
 | 
						|
	if len(js.signatures) == 0 {
 | 
						|
		return nil, errors.New("missing signature")
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(jsSignaturesSorted(js.signatures))
 | 
						|
 | 
						|
	jsonMap := map[string]interface{}{
 | 
						|
		"payload":    js.payload,
 | 
						|
		"signatures": js.signatures,
 | 
						|
	}
 | 
						|
 | 
						|
	return json.MarshalIndent(jsonMap, "", "   ")
 | 
						|
}
 | 
						|
 | 
						|
func notSpace(r rune) bool {
 | 
						|
	return !unicode.IsSpace(r)
 | 
						|
}
 | 
						|
 | 
						|
func detectJSONIndent(jsonContent []byte) (indent string) {
 | 
						|
	if len(jsonContent) > 2 && jsonContent[0] == '{' && jsonContent[1] == '\n' {
 | 
						|
		quoteIndex := bytes.IndexRune(jsonContent[1:], '"')
 | 
						|
		if quoteIndex > 0 {
 | 
						|
			indent = string(jsonContent[2 : quoteIndex+1])
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
type jsParsedHeader struct {
 | 
						|
	JWK       json.RawMessage `json:"jwk"`
 | 
						|
	Algorithm string          `json:"alg"`
 | 
						|
	Chain     []string        `json:"x5c"`
 | 
						|
}
 | 
						|
 | 
						|
type jsParsedSignature struct {
 | 
						|
	Header    jsParsedHeader `json:"header"`
 | 
						|
	Signature string         `json:"signature"`
 | 
						|
	Protected string         `json:"protected"`
 | 
						|
}
 | 
						|
 | 
						|
// ParseJWS parses a JWS serialized JSON object into a Json Signature.
 | 
						|
func ParseJWS(content []byte) (*JSONSignature, error) {
 | 
						|
	type jsParsed struct {
 | 
						|
		Payload    string              `json:"payload"`
 | 
						|
		Signatures []jsParsedSignature `json:"signatures"`
 | 
						|
	}
 | 
						|
	parsed := &jsParsed{}
 | 
						|
	err := json.Unmarshal(content, parsed)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if len(parsed.Signatures) == 0 {
 | 
						|
		return nil, errors.New("missing signatures")
 | 
						|
	}
 | 
						|
	payload, err := joseBase64UrlDecode(parsed.Payload)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	js, err := NewJSONSignature(payload)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	js.signatures = make([]jsSignature, len(parsed.Signatures))
 | 
						|
	for i, signature := range parsed.Signatures {
 | 
						|
		header := jsHeader{
 | 
						|
			Algorithm: signature.Header.Algorithm,
 | 
						|
		}
 | 
						|
		if signature.Header.Chain != nil {
 | 
						|
			header.Chain = signature.Header.Chain
 | 
						|
		}
 | 
						|
		if signature.Header.JWK != nil {
 | 
						|
			publicKey, err := UnmarshalPublicKeyJWK([]byte(signature.Header.JWK))
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			header.JWK = publicKey
 | 
						|
		}
 | 
						|
		js.signatures[i] = jsSignature{
 | 
						|
			Header:    header,
 | 
						|
			Signature: signature.Signature,
 | 
						|
			Protected: signature.Protected,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return js, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewJSONSignature returns a new unsigned JWS from a json byte array.
 | 
						|
// JSONSignature will need to be signed before serializing or storing.
 | 
						|
// Optionally, one or more signatures can be provided as byte buffers,
 | 
						|
// containing serialized JWS signatures, to assemble a fully signed JWS
 | 
						|
// package. It is the callers responsibility to ensure uniqueness of the
 | 
						|
// provided signatures.
 | 
						|
func NewJSONSignature(content []byte, signatures ...[]byte) (*JSONSignature, error) {
 | 
						|
	var dataMap map[string]interface{}
 | 
						|
	err := json.Unmarshal(content, &dataMap)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	js := newJSONSignature()
 | 
						|
	js.indent = detectJSONIndent(content)
 | 
						|
 | 
						|
	js.payload = joseBase64UrlEncode(content)
 | 
						|
 | 
						|
	// Find trailing } and whitespace, put in protected header
 | 
						|
	closeIndex := bytes.LastIndexFunc(content, notSpace)
 | 
						|
	if content[closeIndex] != '}' {
 | 
						|
		return nil, ErrInvalidJSONContent
 | 
						|
	}
 | 
						|
	lastRuneIndex := bytes.LastIndexFunc(content[:closeIndex], notSpace)
 | 
						|
	if content[lastRuneIndex] == ',' {
 | 
						|
		return nil, ErrInvalidJSONContent
 | 
						|
	}
 | 
						|
	js.formatLength = lastRuneIndex + 1
 | 
						|
	js.formatTail = content[js.formatLength:]
 | 
						|
 | 
						|
	if len(signatures) > 0 {
 | 
						|
		for _, signature := range signatures {
 | 
						|
			var parsedJSig jsParsedSignature
 | 
						|
 | 
						|
			if err := json.Unmarshal(signature, &parsedJSig); err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
 | 
						|
			// TODO(stevvooe): A lot of the code below is repeated in
 | 
						|
			// ParseJWS. It will require more refactoring to fix that.
 | 
						|
			jsig := jsSignature{
 | 
						|
				Header: jsHeader{
 | 
						|
					Algorithm: parsedJSig.Header.Algorithm,
 | 
						|
				},
 | 
						|
				Signature: parsedJSig.Signature,
 | 
						|
				Protected: parsedJSig.Protected,
 | 
						|
			}
 | 
						|
 | 
						|
			if parsedJSig.Header.Chain != nil {
 | 
						|
				jsig.Header.Chain = parsedJSig.Header.Chain
 | 
						|
			}
 | 
						|
 | 
						|
			if parsedJSig.Header.JWK != nil {
 | 
						|
				publicKey, err := UnmarshalPublicKeyJWK([]byte(parsedJSig.Header.JWK))
 | 
						|
				if err != nil {
 | 
						|
					return nil, err
 | 
						|
				}
 | 
						|
				jsig.Header.JWK = publicKey
 | 
						|
			}
 | 
						|
 | 
						|
			js.signatures = append(js.signatures, jsig)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return js, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewJSONSignatureFromMap returns a new unsigned JSONSignature from a map or
 | 
						|
// struct. JWS will need to be signed before serializing or storing.
 | 
						|
func NewJSONSignatureFromMap(content interface{}) (*JSONSignature, error) {
 | 
						|
	switch content.(type) {
 | 
						|
	case map[string]interface{}:
 | 
						|
	case struct{}:
 | 
						|
	default:
 | 
						|
		return nil, errors.New("invalid data type")
 | 
						|
	}
 | 
						|
 | 
						|
	js := newJSONSignature()
 | 
						|
	js.indent = "   "
 | 
						|
 | 
						|
	payload, err := json.MarshalIndent(content, "", js.indent)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	js.payload = joseBase64UrlEncode(payload)
 | 
						|
 | 
						|
	// Remove '\n}' from formatted section, put in protected header
 | 
						|
	js.formatLength = len(payload) - 2
 | 
						|
	js.formatTail = payload[js.formatLength:]
 | 
						|
 | 
						|
	return js, nil
 | 
						|
}
 | 
						|
 | 
						|
func readIntFromMap(key string, m map[string]interface{}) (int, bool) {
 | 
						|
	value, ok := m[key]
 | 
						|
	if !ok {
 | 
						|
		return 0, false
 | 
						|
	}
 | 
						|
	switch v := value.(type) {
 | 
						|
	case int:
 | 
						|
		return v, true
 | 
						|
	case float64:
 | 
						|
		return int(v), true
 | 
						|
	default:
 | 
						|
		return 0, false
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func readStringFromMap(key string, m map[string]interface{}) (v string, ok bool) {
 | 
						|
	value, ok := m[key]
 | 
						|
	if !ok {
 | 
						|
		return "", false
 | 
						|
	}
 | 
						|
	v, ok = value.(string)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// ParsePrettySignature parses a formatted signature into a
 | 
						|
// JSON signature. If the signatures are missing the format information
 | 
						|
// an error is thrown. The formatted signature must be created by
 | 
						|
// the same method as format signature.
 | 
						|
func ParsePrettySignature(content []byte, signatureKey string) (*JSONSignature, error) {
 | 
						|
	var contentMap map[string]json.RawMessage
 | 
						|
	err := json.Unmarshal(content, &contentMap)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("error unmarshalling content: %s", err)
 | 
						|
	}
 | 
						|
	sigMessage, ok := contentMap[signatureKey]
 | 
						|
	if !ok {
 | 
						|
		return nil, ErrMissingSignatureKey
 | 
						|
	}
 | 
						|
 | 
						|
	var signatureBlocks []jsParsedSignature
 | 
						|
	err = json.Unmarshal([]byte(sigMessage), &signatureBlocks)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("error unmarshalling signatures: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	js := newJSONSignature()
 | 
						|
	js.signatures = make([]jsSignature, len(signatureBlocks))
 | 
						|
 | 
						|
	for i, signatureBlock := range signatureBlocks {
 | 
						|
		protectedBytes, err := joseBase64UrlDecode(signatureBlock.Protected)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("base64 decode error: %s", err)
 | 
						|
		}
 | 
						|
		var protectedHeader map[string]interface{}
 | 
						|
		err = json.Unmarshal(protectedBytes, &protectedHeader)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("error unmarshalling protected header: %s", err)
 | 
						|
		}
 | 
						|
 | 
						|
		formatLength, ok := readIntFromMap("formatLength", protectedHeader)
 | 
						|
		if !ok {
 | 
						|
			return nil, errors.New("missing formatted length")
 | 
						|
		}
 | 
						|
		encodedTail, ok := readStringFromMap("formatTail", protectedHeader)
 | 
						|
		if !ok {
 | 
						|
			return nil, errors.New("missing formatted tail")
 | 
						|
		}
 | 
						|
		formatTail, err := joseBase64UrlDecode(encodedTail)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("base64 decode error on tail: %s", err)
 | 
						|
		}
 | 
						|
		if js.formatLength == 0 {
 | 
						|
			js.formatLength = formatLength
 | 
						|
		} else if js.formatLength != formatLength {
 | 
						|
			return nil, errors.New("conflicting format length")
 | 
						|
		}
 | 
						|
		if len(js.formatTail) == 0 {
 | 
						|
			js.formatTail = formatTail
 | 
						|
		} else if bytes.Compare(js.formatTail, formatTail) != 0 {
 | 
						|
			return nil, errors.New("conflicting format tail")
 | 
						|
		}
 | 
						|
 | 
						|
		header := jsHeader{
 | 
						|
			Algorithm: signatureBlock.Header.Algorithm,
 | 
						|
			Chain:     signatureBlock.Header.Chain,
 | 
						|
		}
 | 
						|
		if signatureBlock.Header.JWK != nil {
 | 
						|
			publicKey, err := UnmarshalPublicKeyJWK([]byte(signatureBlock.Header.JWK))
 | 
						|
			if err != nil {
 | 
						|
				return nil, fmt.Errorf("error unmarshalling public key: %s", err)
 | 
						|
			}
 | 
						|
			header.JWK = publicKey
 | 
						|
		}
 | 
						|
		js.signatures[i] = jsSignature{
 | 
						|
			Header:    header,
 | 
						|
			Signature: signatureBlock.Signature,
 | 
						|
			Protected: signatureBlock.Protected,
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if js.formatLength > len(content) {
 | 
						|
		return nil, errors.New("invalid format length")
 | 
						|
	}
 | 
						|
	formatted := make([]byte, js.formatLength+len(js.formatTail))
 | 
						|
	copy(formatted, content[:js.formatLength])
 | 
						|
	copy(formatted[js.formatLength:], js.formatTail)
 | 
						|
	js.indent = detectJSONIndent(formatted)
 | 
						|
	js.payload = joseBase64UrlEncode(formatted)
 | 
						|
 | 
						|
	return js, nil
 | 
						|
}
 | 
						|
 | 
						|
// PrettySignature formats a json signature into an easy to read
 | 
						|
// single json serialized object.
 | 
						|
func (js *JSONSignature) PrettySignature(signatureKey string) ([]byte, error) {
 | 
						|
	if len(js.signatures) == 0 {
 | 
						|
		return nil, errors.New("no signatures")
 | 
						|
	}
 | 
						|
	payload, err := joseBase64UrlDecode(js.payload)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	payload = payload[:js.formatLength]
 | 
						|
 | 
						|
	sort.Sort(jsSignaturesSorted(js.signatures))
 | 
						|
 | 
						|
	var marshalled []byte
 | 
						|
	var marshallErr error
 | 
						|
	if js.indent != "" {
 | 
						|
		marshalled, marshallErr = json.MarshalIndent(js.signatures, js.indent, js.indent)
 | 
						|
	} else {
 | 
						|
		marshalled, marshallErr = json.Marshal(js.signatures)
 | 
						|
	}
 | 
						|
	if marshallErr != nil {
 | 
						|
		return nil, marshallErr
 | 
						|
	}
 | 
						|
 | 
						|
	buf := bytes.NewBuffer(make([]byte, 0, len(payload)+len(marshalled)+34))
 | 
						|
	buf.Write(payload)
 | 
						|
	buf.WriteByte(',')
 | 
						|
	if js.indent != "" {
 | 
						|
		buf.WriteByte('\n')
 | 
						|
		buf.WriteString(js.indent)
 | 
						|
		buf.WriteByte('"')
 | 
						|
		buf.WriteString(signatureKey)
 | 
						|
		buf.WriteString("\": ")
 | 
						|
		buf.Write(marshalled)
 | 
						|
		buf.WriteByte('\n')
 | 
						|
	} else {
 | 
						|
		buf.WriteByte('"')
 | 
						|
		buf.WriteString(signatureKey)
 | 
						|
		buf.WriteString("\":")
 | 
						|
		buf.Write(marshalled)
 | 
						|
	}
 | 
						|
	buf.WriteByte('}')
 | 
						|
 | 
						|
	return buf.Bytes(), nil
 | 
						|
}
 | 
						|
 | 
						|
// Signatures provides the signatures on this JWS as opaque blobs, sorted by
 | 
						|
// keyID. These blobs can be stored and reassembled with payloads. Internally,
 | 
						|
// they are simply marshaled json web signatures but implementations should
 | 
						|
// not rely on this.
 | 
						|
func (js *JSONSignature) Signatures() ([][]byte, error) {
 | 
						|
	sort.Sort(jsSignaturesSorted(js.signatures))
 | 
						|
 | 
						|
	var sb [][]byte
 | 
						|
	for _, jsig := range js.signatures {
 | 
						|
		p, err := json.Marshal(jsig)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		sb = append(sb, p)
 | 
						|
	}
 | 
						|
 | 
						|
	return sb, nil
 | 
						|
}
 | 
						|
 | 
						|
// Merge combines the signatures from one or more other signatures into the
 | 
						|
// method receiver. If the payloads differ for any argument, an error will be
 | 
						|
// returned and the receiver will not be modified.
 | 
						|
func (js *JSONSignature) Merge(others ...*JSONSignature) error {
 | 
						|
	merged := js.signatures
 | 
						|
	for _, other := range others {
 | 
						|
		if js.payload != other.payload {
 | 
						|
			return fmt.Errorf("payloads differ from merge target")
 | 
						|
		}
 | 
						|
		merged = append(merged, other.signatures...)
 | 
						|
	}
 | 
						|
 | 
						|
	js.signatures = merged
 | 
						|
	return nil
 | 
						|
}
 |