Blame vendor/golang.org/x/crypto/acme/jws.go

Packit Service 509fd4
// Copyright 2015 The Go Authors. All rights reserved.
Packit Service 509fd4
// Use of this source code is governed by a BSD-style
Packit Service 509fd4
// license that can be found in the LICENSE file.
Packit Service 509fd4
Packit Service 509fd4
package acme
Packit Service 509fd4
Packit Service 509fd4
import (
Packit Service 509fd4
	"crypto"
Packit Service 509fd4
	"crypto/ecdsa"
Packit Service 509fd4
	"crypto/rand"
Packit Service 509fd4
	"crypto/rsa"
Packit Service 509fd4
	"crypto/sha256"
Packit Service 509fd4
	_ "crypto/sha512" // need for EC keys
Packit Service 509fd4
	"encoding/asn1"
Packit Service 509fd4
	"encoding/base64"
Packit Service 509fd4
	"encoding/json"
Packit Service 509fd4
	"fmt"
Packit Service 509fd4
	"math/big"
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
// keyID is the account identity provided by a CA during registration.
Packit Service 509fd4
type keyID string
Packit Service 509fd4
Packit Service 509fd4
// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
Packit Service 509fd4
// See jwsEncodeJSON for details.
Packit Service 509fd4
const noKeyID = keyID("")
Packit Service 509fd4
Packit Service 509fd4
// noPayload indicates jwsEncodeJSON will encode zero-length octet string
Packit Service 509fd4
// in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
Packit Service 509fd4
// authenticated GET requests via POSTing with an empty payload.
Packit Service 509fd4
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
Packit Service 509fd4
const noPayload = ""
Packit Service 509fd4
Packit Service 509fd4
// jwsEncodeJSON signs claimset using provided key and a nonce.
Packit Service 509fd4
// The result is serialized in JSON format containing either kid or jwk
Packit Service 509fd4
// fields based on the provided keyID value.
Packit Service 509fd4
//
Packit Service 509fd4
// If kid is non-empty, its quoted value is inserted in the protected head
Packit Service 509fd4
// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
Packit Service 509fd4
// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
Packit Service 509fd4
//
Packit Service 509fd4
// See https://tools.ietf.org/html/rfc7515#section-7.
Packit Service 509fd4
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
Packit Service 509fd4
	alg, sha := jwsHasher(key.Public())
Packit Service 509fd4
	if alg == "" || !sha.Available() {
Packit Service 509fd4
		return nil, ErrUnsupportedKey
Packit Service 509fd4
	}
Packit Service 509fd4
	var phead string
Packit Service 509fd4
	switch kid {
Packit Service 509fd4
	case noKeyID:
Packit Service 509fd4
		jwk, err := jwkEncode(key.Public())
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return nil, err
Packit Service 509fd4
		}
Packit Service 509fd4
		phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
Packit Service 509fd4
	default:
Packit Service 509fd4
		phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
Packit Service 509fd4
	}
Packit Service 509fd4
	phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
Packit Service 509fd4
	var payload string
Packit Service 509fd4
	if claimset != noPayload {
Packit Service 509fd4
		cs, err := json.Marshal(claimset)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return nil, err
Packit Service 509fd4
		}
Packit Service 509fd4
		payload = base64.RawURLEncoding.EncodeToString(cs)
Packit Service 509fd4
	}
Packit Service 509fd4
	hash := sha.New()
Packit Service 509fd4
	hash.Write([]byte(phead + "." + payload))
Packit Service 509fd4
	sig, err := jwsSign(key, sha, hash.Sum(nil))
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return nil, err
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	enc := struct {
Packit Service 509fd4
		Protected string `json:"protected"`
Packit Service 509fd4
		Payload   string `json:"payload"`
Packit Service 509fd4
		Sig       string `json:"signature"`
Packit Service 509fd4
	}{
Packit Service 509fd4
		Protected: phead,
Packit Service 509fd4
		Payload:   payload,
Packit Service 509fd4
		Sig:       base64.RawURLEncoding.EncodeToString(sig),
Packit Service 509fd4
	}
Packit Service 509fd4
	return json.Marshal(&enc)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
Packit Service 509fd4
// The result is also suitable for creating a JWK thumbprint.
Packit Service 509fd4
// https://tools.ietf.org/html/rfc7517
Packit Service 509fd4
func jwkEncode(pub crypto.PublicKey) (string, error) {
Packit Service 509fd4
	switch pub := pub.(type) {
Packit Service 509fd4
	case *rsa.PublicKey:
Packit Service 509fd4
		// https://tools.ietf.org/html/rfc7518#section-6.3.1
Packit Service 509fd4
		n := pub.N
Packit Service 509fd4
		e := big.NewInt(int64(pub.E))
Packit Service 509fd4
		// Field order is important.
Packit Service 509fd4
		// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
Packit Service 509fd4
		return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
Packit Service 509fd4
			base64.RawURLEncoding.EncodeToString(e.Bytes()),
Packit Service 509fd4
			base64.RawURLEncoding.EncodeToString(n.Bytes()),
Packit Service 509fd4
		), nil
Packit Service 509fd4
	case *ecdsa.PublicKey:
Packit Service 509fd4
		// https://tools.ietf.org/html/rfc7518#section-6.2.1
Packit Service 509fd4
		p := pub.Curve.Params()
Packit Service 509fd4
		n := p.BitSize / 8
Packit Service 509fd4
		if p.BitSize%8 != 0 {
Packit Service 509fd4
			n++
Packit Service 509fd4
		}
Packit Service 509fd4
		x := pub.X.Bytes()
Packit Service 509fd4
		if n > len(x) {
Packit Service 509fd4
			x = append(make([]byte, n-len(x)), x...)
Packit Service 509fd4
		}
Packit Service 509fd4
		y := pub.Y.Bytes()
Packit Service 509fd4
		if n > len(y) {
Packit Service 509fd4
			y = append(make([]byte, n-len(y)), y...)
Packit Service 509fd4
		}
Packit Service 509fd4
		// Field order is important.
Packit Service 509fd4
		// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
Packit Service 509fd4
		return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
Packit Service 509fd4
			p.Name,
Packit Service 509fd4
			base64.RawURLEncoding.EncodeToString(x),
Packit Service 509fd4
			base64.RawURLEncoding.EncodeToString(y),
Packit Service 509fd4
		), nil
Packit Service 509fd4
	}
Packit Service 509fd4
	return "", ErrUnsupportedKey
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// jwsSign signs the digest using the given key.
Packit Service 509fd4
// The hash is unused for ECDSA keys.
Packit Service 509fd4
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
Packit Service 509fd4
	switch pub := key.Public().(type) {
Packit Service 509fd4
	case *rsa.PublicKey:
Packit Service 509fd4
		return key.Sign(rand.Reader, digest, hash)
Packit Service 509fd4
	case *ecdsa.PublicKey:
Packit Service 509fd4
		sigASN1, err := key.Sign(rand.Reader, digest, hash)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return nil, err
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var rs struct{ R, S *big.Int }
Packit Service 509fd4
		if _, err := asn1.Unmarshal(sigASN1, &rs); err != nil {
Packit Service 509fd4
			return nil, err
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		rb, sb := rs.R.Bytes(), rs.S.Bytes()
Packit Service 509fd4
		size := pub.Params().BitSize / 8
Packit Service 509fd4
		if size%8 > 0 {
Packit Service 509fd4
			size++
Packit Service 509fd4
		}
Packit Service 509fd4
		sig := make([]byte, size*2)
Packit Service 509fd4
		copy(sig[size-len(rb):], rb)
Packit Service 509fd4
		copy(sig[size*2-len(sb):], sb)
Packit Service 509fd4
		return sig, nil
Packit Service 509fd4
	}
Packit Service 509fd4
	return nil, ErrUnsupportedKey
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// jwsHasher indicates suitable JWS algorithm name and a hash function
Packit Service 509fd4
// to use for signing a digest with the provided key.
Packit Service 509fd4
// It returns ("", 0) if the key is not supported.
Packit Service 509fd4
func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
Packit Service 509fd4
	switch pub := pub.(type) {
Packit Service 509fd4
	case *rsa.PublicKey:
Packit Service 509fd4
		return "RS256", crypto.SHA256
Packit Service 509fd4
	case *ecdsa.PublicKey:
Packit Service 509fd4
		switch pub.Params().Name {
Packit Service 509fd4
		case "P-256":
Packit Service 509fd4
			return "ES256", crypto.SHA256
Packit Service 509fd4
		case "P-384":
Packit Service 509fd4
			return "ES384", crypto.SHA384
Packit Service 509fd4
		case "P-521":
Packit Service 509fd4
			return "ES512", crypto.SHA512
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return "", 0
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// JWKThumbprint creates a JWK thumbprint out of pub
Packit Service 509fd4
// as specified in https://tools.ietf.org/html/rfc7638.
Packit Service 509fd4
func JWKThumbprint(pub crypto.PublicKey) (string, error) {
Packit Service 509fd4
	jwk, err := jwkEncode(pub)
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return "", err
Packit Service 509fd4
	}
Packit Service 509fd4
	b := sha256.Sum256([]byte(jwk))
Packit Service 509fd4
	return base64.RawURLEncoding.EncodeToString(b[:]), nil
Packit Service 509fd4
}