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