1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package peertls 5 6import ( 7 "bytes" 8 "crypto" 9 "crypto/rand" 10 "crypto/tls" 11 "crypto/x509" 12 "io" 13 14 "github.com/zeebo/errs" 15 16 "storj.io/common/pkcrypto" 17) 18 19const ( 20 // LeafIndex is the index of the leaf certificate in a cert chain (0). 21 LeafIndex = iota 22 // CAIndex is the index of the CA certificate in a cert chain (1). 23 CAIndex 24) 25 26var ( 27 // ErrNotExist is used when a file or directory doesn't exist. 28 ErrNotExist = errs.Class("file or directory not found") 29 // ErrGenerate is used when an error occurred during cert/key generation. 30 ErrGenerate = errs.Class("tls generation") 31 // ErrTLSTemplate is used when an error occurs during tls template generation. 32 ErrTLSTemplate = errs.Class("tls template") 33 // ErrVerifyPeerCert is used when an error occurs during `VerifyPeerCertificate`. 34 ErrVerifyPeerCert = errs.Class("tls peer certificate verification") 35 // ErrVerifyCertificateChain is used when a certificate chain can't be verified from leaf to root 36 // (i.e.: each cert in the chain should be signed by the preceding cert and the root should be self-signed). 37 ErrVerifyCertificateChain = errs.Class("certificate chain signature verification failed") 38 // ErrVerifyCAWhitelist is used when a signature wasn't produced by any CA in the whitelist. 39 ErrVerifyCAWhitelist = errs.Class("not signed by any CA in the whitelist") 40) 41 42// PeerCertVerificationFunc is the signature for a `*tls.Config{}`'s 43// `VerifyPeerCertificate` function. 44type PeerCertVerificationFunc func([][]byte, [][]*x509.Certificate) error 45 46// VerifyPeerFunc combines multiple `*tls.Config#VerifyPeerCertificate` 47// functions and adds certificate parsing. 48func VerifyPeerFunc(next ...PeerCertVerificationFunc) PeerCertVerificationFunc { 49 return func(chain [][]byte, _ [][]*x509.Certificate) error { 50 c, err := pkcrypto.CertsFromDER(chain) 51 if err != nil { 52 return NewNonTemporaryError(ErrVerifyPeerCert.Wrap(err)) 53 } 54 55 for _, n := range next { 56 if n != nil { 57 if err := n(chain, [][]*x509.Certificate{c}); err != nil { 58 return NewNonTemporaryError(ErrVerifyPeerCert.Wrap(err)) 59 } 60 } 61 } 62 return nil 63 } 64} 65 66// VerifyPeerCertChains verifies that the first certificate chain contains certificates 67// which are signed by their respective parents, ending with a self-signed root. 68func VerifyPeerCertChains(_ [][]byte, parsedChains [][]*x509.Certificate) error { 69 return verifyChainSignatures(parsedChains[0]) 70} 71 72// VerifyCAWhitelist verifies that the peer identity's CA was signed by any one 73// of the (certificate authority) certificates in the provided whitelist. 74func VerifyCAWhitelist(cas []*x509.Certificate) PeerCertVerificationFunc { 75 if cas == nil { 76 return nil 77 } 78 return func(_ [][]byte, parsedChains [][]*x509.Certificate) error { 79 for _, ca := range cas { 80 err := verifyCertSignature(ca, parsedChains[0][CAIndex]) 81 if err == nil { 82 return nil 83 } 84 } 85 return ErrVerifyCAWhitelist.New("CA cert") 86 } 87} 88 89// TLSCert creates a tls.Certificate from chains, key and leaf. 90func TLSCert(chain [][]byte, leaf *x509.Certificate, key crypto.PrivateKey) (*tls.Certificate, error) { 91 var err error 92 if leaf == nil { 93 leaf, err = pkcrypto.CertFromDER(chain[LeafIndex]) 94 if err != nil { 95 return nil, err 96 } 97 } 98 99 return &tls.Certificate{ 100 Leaf: leaf, 101 Certificate: chain, 102 PrivateKey: key, 103 }, nil 104} 105 106// WriteChain writes the certificate chain (leaf-first) and extensions to the writer, PEM-encoded. 107func WriteChain(w io.Writer, chain ...*x509.Certificate) error { 108 if len(chain) < 1 { 109 return errs.New("expected at least one certificate for writing") 110 } 111 112 var extErrs errs.Group 113 for _, c := range chain { 114 if err := pkcrypto.WriteCertPEM(w, c); err != nil { 115 return errs.Wrap(err) 116 } 117 } 118 return extErrs.Err() 119} 120 121// ChainBytes returns bytes of the certificate chain (leaf-first) to the writer, PEM-encoded. 122func ChainBytes(chain ...*x509.Certificate) ([]byte, error) { 123 var data bytes.Buffer 124 err := WriteChain(&data, chain...) 125 return data.Bytes(), err 126} 127 128// CreateSelfSignedCertificate creates a new self-signed X.509v3 certificate 129// using fields from the given template. 130// 131// A part of the errors that CreateCertificate can return it can return 132// pkcrypto.ErrUnsuportedKey error. 133func CreateSelfSignedCertificate(key crypto.PrivateKey, template *x509.Certificate) (*x509.Certificate, error) { 134 pubKey, err := pkcrypto.PublicKeyFromPrivate(key) 135 if err != nil { 136 return nil, err 137 } 138 return CreateCertificate(pubKey, key, template, template) 139} 140 141// CreateCertificate creates a new X.509v3 certificate based on a template. 142// The new certificate: 143// 144// * will have the public key given as 'signee' 145// * will be signed by 'signer' (which should be the private key of 'issuer') 146// * will be issued by 'issuer' 147// * will have metadata fields copied from 'template' 148// 149// Returns the new Certificate object. 150func CreateCertificate(signee crypto.PublicKey, signer crypto.PrivateKey, template, issuer *x509.Certificate) (*x509.Certificate, error) { 151 if _, ok := signer.(crypto.Signer); !ok { 152 // x509.CreateCertificate will panic in this case, so check here and make debugging easier 153 return nil, errs.New("can't sign certificate with signer key of type %T", signer) 154 } 155 156 // TODO: should we check for uniqueness? 157 template.ExtraExtensions = append(template.ExtraExtensions, template.Extensions...) 158 cb, err := x509.CreateCertificate( 159 rand.Reader, 160 template, 161 issuer, 162 signee, 163 signer, 164 ) 165 if err != nil { 166 return nil, errs.Wrap(err) 167 } 168 return pkcrypto.CertFromDER(cb) 169} 170