storj/pkg/identity/utils.go
JT Olio 1faeeb49d5 prepare key generation for launch (#979)
* 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
2019-01-07 13:02:22 -05:00

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
}