1// Copyright (C) 2019 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package identity
5
6import (
7	"bytes"
8	"context"
9	"crypto"
10	"crypto/x509"
11	"crypto/x509/pkix"
12	"encoding/asn1"
13	"fmt"
14	"io/ioutil"
15	"path/filepath"
16	"strconv"
17	"strings"
18	"time"
19
20	"github.com/zeebo/errs"
21
22	"storj.io/common/peertls"
23	"storj.io/common/peertls/extensions"
24	"storj.io/common/pkcrypto"
25	"storj.io/common/rpc/rpcpeer"
26	"storj.io/common/storj"
27)
28
29// PeerIdentity represents another peer on the network.
30type PeerIdentity struct {
31	RestChain []*x509.Certificate
32	// CA represents the peer's self-signed CA.
33	CA *x509.Certificate
34	// Leaf represents the leaf they're currently using. The leaf should be
35	// signed by the CA. The leaf is what is used for communication.
36	Leaf *x509.Certificate
37	// The ID taken from the CA public key.
38	ID storj.NodeID
39}
40
41// FullIdentity represents you on the network. In addition to a PeerIdentity,
42// a FullIdentity also has a Key, which a PeerIdentity doesn't have.
43type FullIdentity struct {
44	RestChain []*x509.Certificate
45	// CA represents the peer's self-signed CA. The ID is taken from this cert.
46	CA *x509.Certificate
47	// Leaf represents the leaf they're currently using. The leaf should be
48	// signed by the CA. The leaf is what is used for communication.
49	Leaf *x509.Certificate
50	// The ID taken from the CA public key.
51	ID storj.NodeID
52	// Key is the key this identity uses with the leaf for communication.
53	Key crypto.PrivateKey
54}
55
56// ManageablePeerIdentity is a `PeerIdentity` and its corresponding `FullCertificateAuthority`
57// in a single struct. It is used for making changes to the identity that require CA
58// authorization; e.g. adding extensions.
59type ManageablePeerIdentity struct {
60	*PeerIdentity
61	CA *FullCertificateAuthority
62}
63
64// ManageableFullIdentity is a `FullIdentity` and its corresponding `FullCertificateAuthority`
65// in a single struct. It is used for making changes to the identity that require CA
66// authorization and the leaf private key; e.g. revoking a leaf cert (private key changes).
67type ManageableFullIdentity struct {
68	*FullIdentity
69	CA *FullCertificateAuthority
70}
71
72// SetupConfig allows you to run a set of Responsibilities with the given
73// identity. You can also just load an Identity from disk.
74type SetupConfig struct {
75	CertPath  string `help:"path to the certificate chain for this identity" default:"$IDENTITYDIR/identity.cert" path:"true"`
76	KeyPath   string `help:"path to the private key for this identity" default:"$IDENTITYDIR/identity.key" path:"true"`
77	Overwrite bool   `help:"if true, existing identity certs AND keys will overwritten for" default:"false" setup:"true"`
78	Version   string `help:"semantic version of identity storage format" default:"0"`
79}
80
81// Config allows you to run a set of Responsibilities with the given
82// identity. You can also just load an Identity from disk.
83type Config struct {
84	CertPath string `help:"path to the certificate chain for this identity" default:"$IDENTITYDIR/identity.cert" user:"true" path:"true"`
85	KeyPath  string `help:"path to the private key for this identity" default:"$IDENTITYDIR/identity.key" user:"true" path:"true"`
86}
87
88// PeerConfig allows you to interact with a peer identity (cert, no key) on disk.
89type PeerConfig struct {
90	CertPath string `help:"path to the certificate chain for this identity" default:"$IDENTITYDIR/identity.cert" user:"true" path:"true"`
91}
92
93// FullCertificateAuthorityFromPEM loads a FullIdentity from a certificate chain and
94// private key PEM-encoded bytes.
95func FullCertificateAuthorityFromPEM(chainPEM, keyPEM []byte) (*FullCertificateAuthority, error) {
96	peerCA, err := PeerCertificateAuthorityFromPEM(chainPEM)
97	if err != nil {
98		return nil, err
99	}
100
101	// NB: there shouldn't be multiple keys in the key file but if there
102	// are, this uses the first one
103	key, err := pkcrypto.PrivateKeyFromPEM(keyPEM)
104	if err != nil {
105		return nil, err
106	}
107
108	return &FullCertificateAuthority{
109		RestChain: peerCA.RestChain,
110		Cert:      peerCA.Cert,
111		Key:       key,
112		ID:        peerCA.ID,
113	}, nil
114}
115
116// PeerCertificateAuthorityFromPEM loads a FullIdentity from a certificate chain and
117// private key PEM-encoded bytes.
118func PeerCertificateAuthorityFromPEM(chainPEM []byte) (*PeerCertificateAuthority, error) {
119	chain, err := pkcrypto.CertsFromPEM(chainPEM)
120	if err != nil {
121		return nil, errs.Wrap(err)
122	}
123	// NB: the "leaf" cert in a CA chain is the "CA" cert in an identity chain
124	nodeID, err := NodeIDFromCert(chain[peertls.LeafIndex])
125	if err != nil {
126		return nil, err
127	}
128
129	return &PeerCertificateAuthority{
130		RestChain: chain[peertls.CAIndex:],
131		Cert:      chain[peertls.LeafIndex],
132		ID:        nodeID,
133	}, nil
134}
135
136// FullIdentityFromPEM loads a FullIdentity from a certificate chain and
137// private key PEM-encoded bytes.
138func FullIdentityFromPEM(chainPEM, keyPEM []byte) (*FullIdentity, error) {
139	peerIdent, err := PeerIdentityFromPEM(chainPEM)
140	if err != nil {
141		return nil, err
142	}
143
144	// NB: there shouldn't be multiple keys in the key file but if there
145	// are, this uses the first one
146	key, err := pkcrypto.PrivateKeyFromPEM(keyPEM)
147	if err != nil {
148		return nil, err
149	}
150
151	return &FullIdentity{
152		RestChain: peerIdent.RestChain,
153		CA:        peerIdent.CA,
154		Leaf:      peerIdent.Leaf,
155		Key:       key,
156		ID:        peerIdent.ID,
157	}, nil
158}
159
160// PeerIdentityFromPEM loads a PeerIdentity from a certificate chain and
161// private key PEM-encoded bytes.
162func PeerIdentityFromPEM(chainPEM []byte) (*PeerIdentity, error) {
163	chain, err := pkcrypto.CertsFromPEM(chainPEM)
164	if err != nil {
165		return nil, errs.Wrap(err)
166	}
167	if len(chain) < peertls.CAIndex+1 {
168		return nil, pkcrypto.ErrChainLength.New("identity chain does not contain a CA certificate")
169	}
170	nodeID, err := NodeIDFromCert(chain[peertls.CAIndex])
171	if err != nil {
172		return nil, err
173	}
174
175	return &PeerIdentity{
176		RestChain: chain[peertls.CAIndex+1:],
177		CA:        chain[peertls.CAIndex],
178		Leaf:      chain[peertls.LeafIndex],
179		ID:        nodeID,
180	}, nil
181}
182
183// PeerIdentityFromChain loads a PeerIdentity from an identity certificate chain.
184func PeerIdentityFromChain(chain []*x509.Certificate) (*PeerIdentity, error) {
185	nodeID, err := NodeIDFromCert(chain[peertls.CAIndex])
186	if err != nil {
187		return nil, err
188	}
189
190	return &PeerIdentity{
191		RestChain: chain[peertls.CAIndex+1:],
192		CA:        chain[peertls.CAIndex],
193		ID:        nodeID,
194		Leaf:      chain[peertls.LeafIndex],
195	}, nil
196}
197
198// PeerIdentityFromPeer loads a PeerIdentity from a peer connection.
199func PeerIdentityFromPeer(peer *rpcpeer.Peer) (*PeerIdentity, error) {
200	chain := peer.State.PeerCertificates
201	if len(chain)-1 < peertls.CAIndex {
202		return nil, Error.New("invalid certificate chain")
203	}
204	pi, err := PeerIdentityFromChain(chain)
205	if err != nil {
206		return nil, err
207	}
208	return pi, nil
209}
210
211// PeerIdentityFromContext loads a PeerIdentity from a ctx TLS credentials.
212func PeerIdentityFromContext(ctx context.Context) (*PeerIdentity, error) {
213	peer, err := rpcpeer.FromContext(ctx)
214	if err != nil {
215		return nil, err
216	}
217	return PeerIdentityFromPeer(peer)
218}
219
220// NodeIDFromCertPath loads a node ID from a certificate file path.
221func NodeIDFromCertPath(certPath string) (storj.NodeID, error) {
222	/* #nosec G304 */ // Subsequent calls ensure that the file is a certificate
223	certBytes, err := ioutil.ReadFile(certPath)
224	if err != nil {
225		return storj.NodeID{}, err
226	}
227	return NodeIDFromPEM(certBytes)
228}
229
230// NodeIDFromPEM loads a node ID from certificate bytes.
231func NodeIDFromPEM(pemBytes []byte) (storj.NodeID, error) {
232	chain, err := pkcrypto.CertsFromPEM(pemBytes)
233	if err != nil {
234		return storj.NodeID{}, Error.New("invalid identity certificate")
235	}
236	if len(chain)-1 < peertls.CAIndex {
237		return storj.NodeID{}, Error.New("no CA in identity certificate")
238	}
239	return NodeIDFromCert(chain[peertls.CAIndex])
240}
241
242// NodeIDFromCert looks for a version in an ID version extension in the passed
243// cert and then calculates a versioned node ID using the certificate public key.
244// NB: `cert` would typically be an identity's certificate authority certificate.
245func NodeIDFromCert(cert *x509.Certificate) (id storj.NodeID, err error) {
246	version, err := storj.IDVersionFromCert(cert)
247	if err != nil {
248		return id, Error.Wrap(err)
249	}
250	return NodeIDFromKey(cert.PublicKey, version)
251}
252
253// NodeIDFromKey calculates the node ID for a given public key with the passed version.
254func NodeIDFromKey(k crypto.PublicKey, version storj.IDVersion) (storj.NodeID, error) {
255	idBytes, err := peertls.DoubleSHA256PublicKey(k)
256	if err != nil {
257		return storj.NodeID{}, storj.ErrNodeID.Wrap(err)
258	}
259	return storj.NewVersionedID(idBytes, version), nil
260}
261
262// NewFullIdentity creates a new ID for nodes with difficulty and concurrency params.
263func NewFullIdentity(ctx context.Context, opts NewCAOptions) (*FullIdentity, error) {
264	ca, err := NewCA(ctx, opts)
265	if err != nil {
266		return nil, err
267	}
268	identity, err := ca.NewIdentity()
269	if err != nil {
270		return nil, err
271	}
272	return identity, err
273}
274
275// ToChains takes a number of certificate chains and returns them as a 2d slice of chains of certificates.
276func ToChains(chains ...[]*x509.Certificate) [][]*x509.Certificate {
277	combinedChains := make([][]*x509.Certificate, len(chains))
278	copy(combinedChains, chains)
279	return combinedChains
280}
281
282// NewManageablePeerIdentity returns a manageable identity given a full identity and a full certificate authority.
283func NewManageablePeerIdentity(ident *PeerIdentity, ca *FullCertificateAuthority) *ManageablePeerIdentity {
284	return &ManageablePeerIdentity{
285		PeerIdentity: ident,
286		CA:           ca,
287	}
288}
289
290// NewManageableFullIdentity returns a manageable identity given a full identity and a full certificate authority.
291func NewManageableFullIdentity(ident *FullIdentity, ca *FullCertificateAuthority) *ManageableFullIdentity {
292	return &ManageableFullIdentity{
293		FullIdentity: ident,
294		CA:           ca,
295	}
296}
297
298// Status returns the status of the identity cert/key files for the config.
299func (is SetupConfig) Status() (TLSFilesStatus, error) {
300	return statTLSFiles(is.CertPath, is.KeyPath)
301}
302
303// Create generates and saves a CA using the config.
304func (is SetupConfig) Create(ca *FullCertificateAuthority) (*FullIdentity, error) {
305	fi, err := ca.NewIdentity()
306	if err != nil {
307		return nil, err
308	}
309	fi.CA = ca.Cert
310	ic := Config{
311		CertPath: is.CertPath,
312		KeyPath:  is.KeyPath,
313	}
314	return fi, ic.Save(fi)
315}
316
317// FullConfig converts a `SetupConfig` to `Config`.
318func (is SetupConfig) FullConfig() Config {
319	return Config{
320		CertPath: is.CertPath,
321		KeyPath:  is.KeyPath,
322	}
323}
324
325// Load loads a FullIdentity from the config.
326func (ic Config) Load() (*FullIdentity, error) {
327	c, err := ioutil.ReadFile(ic.CertPath)
328	if err != nil {
329		return nil, peertls.ErrNotExist.Wrap(err)
330	}
331	k, err := ioutil.ReadFile(ic.KeyPath)
332	if err != nil {
333		return nil, peertls.ErrNotExist.Wrap(err)
334	}
335	fi, err := FullIdentityFromPEM(c, k)
336	if err != nil {
337		return nil, errs.New("failed to load identity %#v, %#v: %v",
338			ic.CertPath, ic.KeyPath, err)
339	}
340	return fi, nil
341}
342
343// Save saves a FullIdentity according to the config.
344func (ic Config) Save(fi *FullIdentity) error {
345	var (
346		certData, keyData                                              bytes.Buffer
347		writeChainErr, writeChainDataErr, writeKeyErr, writeKeyDataErr error
348	)
349
350	chain := []*x509.Certificate{fi.Leaf, fi.CA}
351	chain = append(chain, fi.RestChain...)
352
353	if ic.CertPath != "" {
354		writeChainErr = peertls.WriteChain(&certData, chain...)
355		writeChainDataErr = writeChainData(ic.CertPath, certData.Bytes())
356	}
357
358	if ic.KeyPath != "" {
359		writeKeyErr = pkcrypto.WritePrivateKeyPEM(&keyData, fi.Key)
360		writeKeyDataErr = writeKeyData(ic.KeyPath, keyData.Bytes())
361	}
362
363	writeErr := errs.Combine(writeChainErr, writeKeyErr)
364	if writeErr != nil {
365		return writeErr
366	}
367
368	return errs.Combine(
369		writeChainDataErr,
370		writeKeyDataErr,
371	)
372}
373
374// SaveBackup saves the certificate of the config with a timestamped filename.
375func (ic Config) SaveBackup(fi *FullIdentity) error {
376	return Config{
377		CertPath: backupPath(ic.CertPath),
378		KeyPath:  backupPath(ic.KeyPath),
379	}.Save(fi)
380}
381
382// PeerConfig converts a Config to a PeerConfig.
383func (ic Config) PeerConfig() *PeerConfig {
384	return &PeerConfig{
385		CertPath: ic.CertPath,
386	}
387}
388
389// Load loads a PeerIdentity from the config.
390func (ic PeerConfig) Load() (*PeerIdentity, error) {
391	c, err := ioutil.ReadFile(ic.CertPath)
392	if err != nil {
393		return nil, peertls.ErrNotExist.Wrap(err)
394	}
395	pi, err := PeerIdentityFromPEM(c)
396	if err != nil {
397		return nil, errs.New("failed to load identity %#v: %v", ic.CertPath, err)
398	}
399	return pi, nil
400}
401
402// Save saves a PeerIdentity according to the config.
403func (ic PeerConfig) Save(peerIdent *PeerIdentity) error {
404	chain := []*x509.Certificate{peerIdent.Leaf, peerIdent.CA}
405	chain = append(chain, peerIdent.RestChain...)
406
407	if ic.CertPath != "" {
408		var certData bytes.Buffer
409		err := peertls.WriteChain(&certData, chain...)
410		if err != nil {
411			return err
412		}
413
414		return writeChainData(ic.CertPath, certData.Bytes())
415	}
416
417	return nil
418}
419
420// SaveBackup saves the certificate of the config with a timestamped filename.
421func (ic PeerConfig) SaveBackup(pi *PeerIdentity) error {
422	return PeerConfig{
423		CertPath: backupPath(ic.CertPath),
424	}.Save(pi)
425}
426
427// Chain returns the Identity's certificate chain.
428func (fi *FullIdentity) Chain() []*x509.Certificate {
429	return append([]*x509.Certificate{fi.Leaf, fi.CA}, fi.RestChain...)
430}
431
432// RawChain returns all of the certificate chain as a 2d byte slice.
433func (fi *FullIdentity) RawChain() [][]byte {
434	chain := fi.Chain()
435	rawChain := make([][]byte, len(chain))
436	for i, cert := range chain {
437		rawChain[i] = cert.Raw
438	}
439	return rawChain
440}
441
442// RawRestChain returns the rest (excluding leaf and CA) of the certificate chain as a 2d byte slice.
443func (fi *FullIdentity) RawRestChain() [][]byte {
444	rawChain := make([][]byte, len(fi.RestChain))
445	for i, cert := range fi.RestChain {
446		rawChain[i] = cert.Raw
447	}
448	return rawChain
449}
450
451// PeerIdentity converts a FullIdentity into a PeerIdentity.
452func (fi *FullIdentity) PeerIdentity() *PeerIdentity {
453	return &PeerIdentity{
454		CA:        fi.CA,
455		Leaf:      fi.Leaf,
456		ID:        fi.ID,
457		RestChain: fi.RestChain,
458	}
459}
460
461// Version looks up the version based on the certificate's ID version extension.
462func (fi *FullIdentity) Version() (storj.IDVersion, error) {
463	return storj.IDVersionFromCert(fi.CA)
464}
465
466// AddExtension adds extensions to the leaf cert of an identity. Extensions
467// are serialized into the certificate's raw bytes and is re-signed by it's
468// certificate authority.
469func (manageableIdent *ManageablePeerIdentity) AddExtension(ext ...pkix.Extension) error {
470	if err := extensions.AddExtraExtension(manageableIdent.Leaf, ext...); err != nil {
471		return err
472	}
473
474	updatedCert, err := peertls.CreateCertificate(manageableIdent.Leaf.PublicKey, manageableIdent.CA.Key, manageableIdent.Leaf, manageableIdent.CA.Cert)
475	if err != nil {
476		return err
477	}
478
479	manageableIdent.Leaf = updatedCert
480	return nil
481}
482
483// Revoke extends the CA certificate with a certificate revocation extension.
484func (manageableIdent *ManageableFullIdentity) Revoke() error {
485	ext, err := extensions.NewRevocationExt(manageableIdent.CA.Key, manageableIdent.Leaf)
486	if err != nil {
487		return err
488	}
489
490	revokingIdent, err := manageableIdent.CA.NewIdentity(ext)
491	if err != nil {
492		return err
493	}
494
495	manageableIdent.Leaf = revokingIdent.Leaf
496
497	return nil
498}
499
500func backupPath(path string) string {
501	pathExt := filepath.Ext(path)
502	base := strings.TrimSuffix(path, pathExt)
503	return fmt.Sprintf(
504		"%s.%s%s",
505		base,
506		strconv.Itoa(int(time.Now().Unix())),
507		pathExt,
508	)
509}
510
511// EncodePeerIdentity encodes the complete identity chain to bytes.
512func EncodePeerIdentity(pi *PeerIdentity) []byte {
513	var chain []byte
514	chain = append(chain, pi.Leaf.Raw...)
515	chain = append(chain, pi.CA.Raw...)
516	for _, cert := range pi.RestChain {
517		chain = append(chain, cert.Raw...)
518	}
519	return chain
520}
521
522// DecodePeerIdentity Decodes the bytes into complete identity chain.
523func DecodePeerIdentity(ctx context.Context, chain []byte) (_ *PeerIdentity, err error) {
524	defer mon.Task()(&ctx)(&err)
525
526	var certs []*x509.Certificate
527	for len(chain) > 0 {
528		var raw asn1.RawValue
529		var err error
530
531		chain, err = asn1.Unmarshal(chain, &raw)
532		if err != nil {
533			return nil, Error.Wrap(err)
534		}
535
536		cert, err := pkcrypto.CertFromDER(raw.FullBytes)
537		if err != nil {
538			return nil, Error.Wrap(err)
539		}
540
541		certs = append(certs, cert)
542	}
543	if len(certs) < 2 {
544		return nil, Error.New("not enough certificates")
545	}
546	return PeerIdentityFromChain(certs)
547}
548