1faeeb49d5
* pkg/identity: use sha256 instead of sha3 for pow Change-Id: I9b7a4f2c3e624a6e248a233e3653eaccaf23c6f3 * pkg/identity: restructure key generation a bit Change-Id: I0061a5cc62f04b0c86ffbf046519d5c0a154e896 * cmd/identity: indefinite key generation command you can start this command and leave it running and it will fill up your hard drive with node certificate authority private keys ordered by difficulty. Change-Id: I61c7a3438b9ff6656e74b8d74fef61e557e4d95a * pkg/storj: more node id difficulty testing Change-Id: Ie56b1859aa14ec6ef5973caf42aacb4c494b87c7 * review comments Change-Id: Iff019aa8121a7804f10c248bf2e578189e5b829d
193 lines
4.0 KiB
Go
193 lines
4.0 KiB
Go
// Copyright (C) 2018 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package identity
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/pem"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"storj.io/storj/pkg/peertls"
|
|
"storj.io/storj/pkg/utils"
|
|
)
|
|
|
|
// TLSFilesStatus is the status of keys
|
|
type TLSFilesStatus int
|
|
|
|
// Four possible outcomes for four files
|
|
const (
|
|
NoCertNoKey = TLSFilesStatus(iota)
|
|
CertNoKey
|
|
NoCertKey
|
|
CertKey
|
|
)
|
|
|
|
var (
|
|
// ErrChainLength is used when the length of a cert chain isn't what was expected
|
|
ErrChainLength = errs.Class("cert chain length error")
|
|
// ErrZeroBytes is returned for zero slice
|
|
ErrZeroBytes = errs.New("byte slice was unexpectedly empty")
|
|
)
|
|
|
|
type encodedChain struct {
|
|
chain [][]byte
|
|
extensions [][][]byte
|
|
}
|
|
|
|
// DecodeAndParseChainPEM parses a PEM chain
|
|
func DecodeAndParseChainPEM(PEMBytes []byte) ([]*x509.Certificate, error) {
|
|
var (
|
|
encChain encodedChain
|
|
blockErrs utils.ErrorGroup
|
|
)
|
|
for {
|
|
var pemBlock *pem.Block
|
|
pemBlock, PEMBytes = pem.Decode(PEMBytes)
|
|
if pemBlock == nil {
|
|
break
|
|
}
|
|
switch pemBlock.Type {
|
|
case peertls.BlockTypeCertificate:
|
|
encChain.AddCert(pemBlock.Bytes)
|
|
case peertls.BlockTypeExtension:
|
|
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
|
|
blockErrs.Add(err)
|
|
}
|
|
}
|
|
}
|
|
if err := blockErrs.Finish(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return encChain.Parse()
|
|
}
|
|
|
|
func decodePEM(PEMBytes []byte) ([][]byte, error) {
|
|
var DERBytes [][]byte
|
|
|
|
for {
|
|
var DERBlock *pem.Block
|
|
|
|
DERBlock, PEMBytes = pem.Decode(PEMBytes)
|
|
if DERBlock == nil {
|
|
break
|
|
}
|
|
|
|
DERBytes = append(DERBytes, DERBlock.Bytes)
|
|
}
|
|
|
|
if len(DERBytes) == 0 || len(DERBytes[0]) == 0 {
|
|
return nil, ErrZeroBytes
|
|
}
|
|
|
|
return DERBytes, nil
|
|
}
|
|
|
|
// writeChainData writes data to path ensuring permissions are appropriate for a cert
|
|
func writeChainData(path string, data []byte) error {
|
|
err := writeFile(path, 0744, 0644, data)
|
|
if err != nil {
|
|
return errs.New("unable to write certificate to \"%s\": %v", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeKeyData writes data to path ensuring permissions are appropriate for a cert
|
|
func writeKeyData(path string, data []byte) error {
|
|
err := writeFile(path, 0700, 0600, data)
|
|
if err != nil {
|
|
return errs.New("unable to write key to \"%s\": %v", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeFile writes to path, creating directories and files with the necessary permissions
|
|
func writeFile(path string, dirmode, filemode os.FileMode, data []byte) error {
|
|
if err := os.MkdirAll(filepath.Dir(path), dirmode); err != nil {
|
|
return errs.Wrap(err)
|
|
}
|
|
|
|
if err := ioutil.WriteFile(path, data, filemode); err != nil {
|
|
return errs.Wrap(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func statTLSFiles(certPath, keyPath string) TLSFilesStatus {
|
|
_, err := os.Stat(certPath)
|
|
hasCert := os.IsExist(err)
|
|
|
|
_, err = os.Stat(keyPath)
|
|
hasKey := os.IsExist(err)
|
|
|
|
if hasCert && hasKey {
|
|
return CertKey
|
|
} else if hasCert {
|
|
return CertNoKey
|
|
} else if hasKey {
|
|
return NoCertKey
|
|
}
|
|
|
|
return NoCertNoKey
|
|
}
|
|
|
|
func (t TLSFilesStatus) String() string {
|
|
switch t {
|
|
case CertKey:
|
|
return "certificate and key"
|
|
case CertNoKey:
|
|
return "certificate"
|
|
case NoCertKey:
|
|
return "key"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (e *encodedChain) AddCert(b []byte) {
|
|
e.chain = append(e.chain, b)
|
|
e.extensions = append(e.extensions, [][]byte{})
|
|
}
|
|
|
|
func (e *encodedChain) AddExtension(b []byte) error {
|
|
chainLen := len(e.chain)
|
|
if chainLen < 1 {
|
|
return ErrChainLength.New("expected: >= 1; actual: %d", chainLen)
|
|
}
|
|
|
|
i := chainLen - 1
|
|
e.extensions[i] = append(e.extensions[i], b)
|
|
return nil
|
|
}
|
|
|
|
func (e *encodedChain) Parse() ([]*x509.Certificate, error) {
|
|
chain, err := ParseCertChain(e.chain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var extErrs utils.ErrorGroup
|
|
for i, cert := range chain {
|
|
for _, ee := range e.extensions[i] {
|
|
ext := pkix.Extension{}
|
|
_, err := asn1.Unmarshal(ee, &ext)
|
|
if err != nil {
|
|
extErrs.Add(err)
|
|
}
|
|
cert.ExtraExtensions = append(cert.ExtraExtensions, ext)
|
|
}
|
|
}
|
|
if err := extErrs.Finish(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return chain, nil
|
|
}
|