...

Source file src/github.com/in-toto/in-toto-golang/internal/test/ca.go

Documentation: github.com/in-toto/in-toto-golang/internal/test

     1  package test
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"crypto/x509/pkix"
    11  	"fmt"
    12  	"math/big"
    13  	"net/url"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/spiffe/go-spiffe/v2/bundle/spiffebundle"
    18  	"github.com/spiffe/go-spiffe/v2/bundle/x509bundle"
    19  	"github.com/spiffe/go-spiffe/v2/spiffeid"
    20  	"github.com/spiffe/go-spiffe/v2/svid/x509svid"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  type CA struct {
    25  	tb     testing.TB
    26  	td     spiffeid.TrustDomain
    27  	parent *CA
    28  	cert   *x509.Certificate
    29  	key    crypto.Signer
    30  	jwtKey crypto.Signer
    31  	jwtKid string
    32  }
    33  
    34  type CertificateOption interface {
    35  	apply(*x509.Certificate)
    36  }
    37  
    38  type certificateOption func(*x509.Certificate)
    39  
    40  func (co certificateOption) apply(c *x509.Certificate) {
    41  	co(c)
    42  }
    43  
    44  // NewEC256Key returns an ECDSA key over the P256 curve
    45  func NewEC256Key(tb testing.TB) *ecdsa.PrivateKey {
    46  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    47  	require.NoError(tb, err)
    48  	return key
    49  }
    50  
    51  // NewKeyID returns a random id useful for identifying keys
    52  func NewKeyID(tb testing.TB) string {
    53  	choices := make([]byte, 32)
    54  	_, err := rand.Read(choices)
    55  	require.NoError(tb, err)
    56  	return keyIDFromBytes(choices)
    57  }
    58  
    59  func keyIDFromBytes(choices []byte) string {
    60  	const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    61  	buf := new(bytes.Buffer)
    62  	for _, choice := range choices {
    63  		buf.WriteByte(alphabet[int(choice)%len(alphabet)])
    64  	}
    65  	return buf.String()
    66  }
    67  
    68  func NewCA(tb testing.TB, td spiffeid.TrustDomain) *CA {
    69  	cert, key := CreateCACertificate(tb, nil, nil)
    70  	return &CA{
    71  		tb:     tb,
    72  		td:     td,
    73  		cert:   cert,
    74  		key:    key,
    75  		jwtKey: NewEC256Key(tb),
    76  		jwtKid: NewKeyID(tb),
    77  	}
    78  }
    79  
    80  func (ca *CA) ChildCA(options ...CertificateOption) *CA {
    81  	cert, key := CreateCACertificate(ca.tb, ca.cert, ca.key, options...)
    82  	return &CA{
    83  		tb:     ca.tb,
    84  		parent: ca,
    85  		cert:   cert,
    86  		key:    key,
    87  		jwtKey: NewEC256Key(ca.tb),
    88  		jwtKid: NewKeyID(ca.tb),
    89  	}
    90  }
    91  
    92  func (ca *CA) CreateX509SVID(id spiffeid.ID, options ...CertificateOption) *x509svid.SVID {
    93  	cert, key := CreateX509SVID(ca.tb, ca.cert, ca.key, id, options...)
    94  	return &x509svid.SVID{
    95  		ID:           id,
    96  		Certificates: append([]*x509.Certificate{cert}, ca.chain(false)...),
    97  		PrivateKey:   key,
    98  	}
    99  }
   100  
   101  func (ca *CA) CreateX509SVIDNoPrivateKey(id spiffeid.ID, options ...CertificateOption) *x509svid.SVID {
   102  	cert, _ := CreateX509SVID(ca.tb, ca.cert, ca.key, id, options...)
   103  	return &x509svid.SVID{
   104  		ID:           id,
   105  		Certificates: append([]*x509.Certificate{cert}, ca.chain(false)...),
   106  	}
   107  }
   108  
   109  func (ca *CA) CreateX509Certificate(options ...CertificateOption) ([]*x509.Certificate, crypto.Signer) {
   110  	cert, key := CreateX509Certificate(ca.tb, ca.cert, ca.key, options...)
   111  	return append([]*x509.Certificate{cert}, ca.chain(false)...), key
   112  }
   113  
   114  func (ca *CA) X509Authorities() []*x509.Certificate {
   115  	root := ca
   116  	for root.parent != nil {
   117  		root = root.parent
   118  	}
   119  	return []*x509.Certificate{root.cert}
   120  }
   121  
   122  func (ca *CA) Bundle() *spiffebundle.Bundle {
   123  	bundle := spiffebundle.New(ca.td)
   124  	bundle.SetX509Authorities(ca.X509Authorities())
   125  	return bundle
   126  }
   127  
   128  func (ca *CA) X509Bundle() *x509bundle.Bundle {
   129  	return x509bundle.FromX509Authorities(ca.td, ca.X509Authorities())
   130  }
   131  
   132  func CreateCACertificate(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
   133  	now := time.Now()
   134  	serial := NewSerial(tb)
   135  	key := NewEC256Key(tb)
   136  	tmpl := &x509.Certificate{
   137  		SerialNumber: serial,
   138  		Subject: pkix.Name{
   139  			CommonName: fmt.Sprintf("CA %x", serial),
   140  		},
   141  		BasicConstraintsValid: true,
   142  		IsCA:                  true,
   143  		NotBefore:             now,
   144  		NotAfter:              now.Add(time.Hour),
   145  	}
   146  
   147  	applyOptions(tmpl, options...)
   148  
   149  	if parent == nil {
   150  		parent = tmpl
   151  		parentKey = key
   152  	}
   153  	return CreateCertificate(tb, tmpl, parent, key.Public(), parentKey), key
   154  }
   155  
   156  func CreateX509Certificate(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
   157  	now := time.Now()
   158  	serial := NewSerial(tb)
   159  	key := NewEC256Key(tb)
   160  	tmpl := &x509.Certificate{
   161  		SerialNumber: serial,
   162  		Subject: pkix.Name{
   163  			CommonName: fmt.Sprintf("X509-Certificate %x", serial),
   164  		},
   165  		NotBefore: now,
   166  		NotAfter:  now.Add(time.Hour),
   167  		KeyUsage:  x509.KeyUsageDigitalSignature,
   168  	}
   169  
   170  	applyOptions(tmpl, options...)
   171  
   172  	return CreateCertificate(tb, tmpl, parent, key.Public(), parentKey), key
   173  }
   174  
   175  func CreateX509SVID(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, id spiffeid.ID, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
   176  	serial := NewSerial(tb)
   177  	options = append(options,
   178  		WithSerial(serial),
   179  		WithKeyUsage(x509.KeyUsageDigitalSignature),
   180  		WithSubject(pkix.Name{
   181  			CommonName: fmt.Sprintf("X509-SVID %x", serial),
   182  		}),
   183  		WithURIs(id.URL()))
   184  
   185  	return CreateX509Certificate(tb, parent, parentKey, options...)
   186  }
   187  
   188  func CreateCertificate(tb testing.TB, tmpl, parent *x509.Certificate, pub, priv interface{}) *x509.Certificate {
   189  	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, parent, pub, priv)
   190  	require.NoError(tb, err)
   191  	cert, err := x509.ParseCertificate(certDER)
   192  	require.NoError(tb, err)
   193  	return cert
   194  }
   195  
   196  func NewSerial(tb testing.TB) *big.Int {
   197  	b := make([]byte, 8)
   198  	_, err := rand.Read(b)
   199  	require.NoError(tb, err)
   200  	return new(big.Int).SetBytes(b)
   201  }
   202  
   203  func WithSerial(serial *big.Int) CertificateOption {
   204  	return certificateOption(func(c *x509.Certificate) {
   205  		c.SerialNumber = serial
   206  	})
   207  }
   208  
   209  func WithKeyUsage(keyUsage x509.KeyUsage) CertificateOption {
   210  	return certificateOption(func(c *x509.Certificate) {
   211  		c.KeyUsage = keyUsage
   212  	})
   213  }
   214  
   215  func WithURIs(uris ...*url.URL) CertificateOption {
   216  	return certificateOption(func(c *x509.Certificate) {
   217  		c.URIs = uris
   218  	})
   219  }
   220  
   221  func WithSubject(subject pkix.Name) CertificateOption {
   222  	return certificateOption(func(c *x509.Certificate) {
   223  		c.Subject = subject
   224  	})
   225  }
   226  
   227  func applyOptions(c *x509.Certificate, options ...CertificateOption) {
   228  	for _, opt := range options {
   229  		opt.apply(c)
   230  	}
   231  }
   232  
   233  func (ca *CA) chain(includeRoot bool) []*x509.Certificate {
   234  	chain := []*x509.Certificate{}
   235  	next := ca
   236  	for next != nil {
   237  		if includeRoot || next.parent != nil {
   238  			chain = append(chain, next.cert)
   239  		}
   240  		next = next.parent
   241  	}
   242  	return chain
   243  }
   244  

View as plain text