github.com/devops-filetransfer/sshego@v7.0.4+incompatible/_vendor/golang.org/x/crypto/acme/jws.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package acme
     6  
     7  import (
     8  	"crypto"
     9  	"crypto/ecdsa"
    10  	"crypto/rand"
    11  	"crypto/rsa"
    12  	"crypto/sha256"
    13  	_ "crypto/sha512" // need for EC keys
    14  	"encoding/base64"
    15  	"encoding/json"
    16  	"fmt"
    17  	"math/big"
    18  )
    19  
    20  // jwsEncodeJSON signs claimset using provided key and a nonce.
    21  // The result is serialized in JSON format.
    22  // See https://tools.ietf.org/html/rfc7515#section-7.
    23  func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
    24  	jwk, err := jwkEncode(key.Public())
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	alg, sha := jwsHasher(key)
    29  	if alg == "" || !sha.Available() {
    30  		return nil, ErrUnsupportedKey
    31  	}
    32  	phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
    33  	phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
    34  	cs, err := json.Marshal(claimset)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	payload := base64.RawURLEncoding.EncodeToString(cs)
    39  	hash := sha.New()
    40  	hash.Write([]byte(phead + "." + payload))
    41  	sig, err := jwsSign(key, sha, hash.Sum(nil))
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	enc := struct {
    47  		Protected string `json:"protected"`
    48  		Payload   string `json:"payload"`
    49  		Sig       string `json:"signature"`
    50  	}{
    51  		Protected: phead,
    52  		Payload:   payload,
    53  		Sig:       base64.RawURLEncoding.EncodeToString(sig),
    54  	}
    55  	return json.Marshal(&enc)
    56  }
    57  
    58  // jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
    59  // The result is also suitable for creating a JWK thumbprint.
    60  // https://tools.ietf.org/html/rfc7517
    61  func jwkEncode(pub crypto.PublicKey) (string, error) {
    62  	switch pub := pub.(type) {
    63  	case *rsa.PublicKey:
    64  		// https://tools.ietf.org/html/rfc7518#section-6.3.1
    65  		n := pub.N
    66  		e := big.NewInt(int64(pub.E))
    67  		// Field order is important.
    68  		// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
    69  		return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
    70  			base64.RawURLEncoding.EncodeToString(e.Bytes()),
    71  			base64.RawURLEncoding.EncodeToString(n.Bytes()),
    72  		), nil
    73  	case *ecdsa.PublicKey:
    74  		// https://tools.ietf.org/html/rfc7518#section-6.2.1
    75  		p := pub.Curve.Params()
    76  		n := p.BitSize / 8
    77  		if p.BitSize%8 != 0 {
    78  			n++
    79  		}
    80  		x := pub.X.Bytes()
    81  		if n > len(x) {
    82  			x = append(make([]byte, n-len(x)), x...)
    83  		}
    84  		y := pub.Y.Bytes()
    85  		if n > len(y) {
    86  			y = append(make([]byte, n-len(y)), y...)
    87  		}
    88  		// Field order is important.
    89  		// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
    90  		return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
    91  			p.Name,
    92  			base64.RawURLEncoding.EncodeToString(x),
    93  			base64.RawURLEncoding.EncodeToString(y),
    94  		), nil
    95  	}
    96  	return "", ErrUnsupportedKey
    97  }
    98  
    99  // jwsSign signs the digest using the given key.
   100  // It returns ErrUnsupportedKey if the key type is unknown.
   101  // The hash is used only for RSA keys.
   102  func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
   103  	switch key := key.(type) {
   104  	case *rsa.PrivateKey:
   105  		return key.Sign(rand.Reader, digest, hash)
   106  	case *ecdsa.PrivateKey:
   107  		r, s, err := ecdsa.Sign(rand.Reader, key, digest)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		rb, sb := r.Bytes(), s.Bytes()
   112  		size := key.Params().BitSize / 8
   113  		if size%8 > 0 {
   114  			size++
   115  		}
   116  		sig := make([]byte, size*2)
   117  		copy(sig[size-len(rb):], rb)
   118  		copy(sig[size*2-len(sb):], sb)
   119  		return sig, nil
   120  	}
   121  	return nil, ErrUnsupportedKey
   122  }
   123  
   124  // jwsHasher indicates suitable JWS algorithm name and a hash function
   125  // to use for signing a digest with the provided key.
   126  // It returns ("", 0) if the key is not supported.
   127  func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
   128  	switch key := key.(type) {
   129  	case *rsa.PrivateKey:
   130  		return "RS256", crypto.SHA256
   131  	case *ecdsa.PrivateKey:
   132  		switch key.Params().Name {
   133  		case "P-256":
   134  			return "ES256", crypto.SHA256
   135  		case "P-384":
   136  			return "ES384", crypto.SHA384
   137  		case "P-521":
   138  			return "ES512", crypto.SHA512
   139  		}
   140  	}
   141  	return "", 0
   142  }
   143  
   144  // JWKThumbprint creates a JWK thumbprint out of pub
   145  // as specified in https://tools.ietf.org/html/rfc7638.
   146  func JWKThumbprint(pub crypto.PublicKey) (string, error) {
   147  	jwk, err := jwkEncode(pub)
   148  	if err != nil {
   149  		return "", err
   150  	}
   151  	b := sha256.Sum256([]byte(jwk))
   152  	return base64.RawURLEncoding.EncodeToString(b[:]), nil
   153  }