storj/pkg/provider/utils.go
Bryan White 2a0c4e60d2
preparing for use of customtype gogo extension with NodeID type (#693)
* preparing for use of `customtype` gogo extension with `NodeID` type

* review changes

* preparing for use of `customtype` gogo extension with `NodeID` type

* review changes

* wip

* tests passing

* wip fixing tests

* more wip test fixing

* remove NodeIDList from proto files

* linter fixes

* linter fixes

* linter/review fixes

* more freaking linter fixes

* omg just kill me - linterrrrrrrr

* travis linter, i will muder you and your family in your sleep

* goimports everything - burn in hell travis

* goimports update

* go mod tidy
2018-11-29 19:39:27 +01:00

224 lines
4.2 KiB
Go

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package provider
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"os"
"path/filepath"
"github.com/zeebo/errs"
"storj.io/storj/pkg/storj"
"storj.io/storj/pkg/peertls"
)
// TLSFilesStatus is the status of keys
type TLSFilesStatus int
// Four possible outcomes for four files
const (
NoCertNoKey = TLSFilesStatus(iota)
CertNoKey
NoCertKey
CertKey
)
var (
// ErrZeroBytes is returned for zero slice
ErrZeroBytes = errs.New("byte slice was unexpectedly empty")
)
func decodePEM(PEMBytes []byte) ([][]byte, error) {
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
}
func newCAWorker(ctx context.Context, difficulty uint16, parentCert *x509.Certificate, parentKey crypto.PrivateKey, caC chan FullCertificateAuthority, eC chan error) {
var (
k crypto.PrivateKey
i storj.NodeID
err error
)
for {
select {
case <-ctx.Done():
return
default:
k, err = peertls.NewKey()
if err != nil {
eC <- err
return
}
switch kE := k.(type) {
case *ecdsa.PrivateKey:
i, err = NodeIDFromKey(&kE.PublicKey)
if err != nil {
eC <- err
return
}
default:
eC <- peertls.ErrUnsupportedKey.New("%T", k)
return
}
}
d, err := i.Difficulty()
if err != nil {
eC <- err
continue
}
if d >= difficulty {
break
}
}
ct, err := peertls.CATemplate()
if err != nil {
eC <- err
return
}
c, err := newCACert(k, parentKey, ct, parentCert)
if err != nil {
eC <- err
return
}
ca := FullCertificateAuthority{
Cert: c,
Key: k,
ID: i,
}
if parentCert != nil {
ca.RestChain = []*x509.Certificate{parentCert}
}
caC <- ca
}
func newCACert(key, parentKey crypto.PrivateKey, template, parentCert *x509.Certificate) (*x509.Certificate, error) {
p, ok := key.(*ecdsa.PrivateKey)
if !ok {
return nil, peertls.ErrUnsupportedKey.New("%T", key)
}
var signingKey crypto.PrivateKey
if parentKey != nil {
signingKey = parentKey
} else {
signingKey = key
}
cert, err := peertls.NewCert(template, parentCert, &p.PublicKey, signingKey)
if err != nil {
return nil, err
}
if parentKey != nil {
p, ok := parentKey.(*ecdsa.PrivateKey)
if !ok {
return nil, peertls.ErrUnsupportedKey.New("%T", key)
}
hash := crypto.SHA256.New()
_, err := hash.Write(cert.RawTBSCertificate)
if err != nil {
return nil, peertls.ErrSign.Wrap(err)
}
r, s, err := ecdsa.Sign(rand.Reader, p, hash.Sum(nil))
if err != nil {
return nil, peertls.ErrSign.Wrap(err)
}
signature, err := asn1.Marshal(peertls.ECDSASignature{R: r, S: s})
if err != nil {
return nil, peertls.ErrSign.Wrap(err)
}
cert.ExtraExtensions = append(cert.ExtraExtensions, pkix.Extension{
Id: peertls.AuthoritySignatureExtID,
Value: signature,
})
}
return cert, nil
}
func openCert(path string, flag int) (*os.File, error) {
if err := os.MkdirAll(filepath.Dir(path), 0744); err != nil {
return nil, errs.Wrap(err)
}
c, err := os.OpenFile(path, flag, 0644)
if err != nil {
return nil, errs.New("unable to open cert file for writing \"%s\": %v", path, err)
}
return c, nil
}
func openKey(path string, flag int) (*os.File, error) {
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return nil, errs.Wrap(err)
}
k, err := os.OpenFile(path, flag, 0600)
if err != nil {
return nil, errs.New("unable to open key file for writing \"%s\": %v", path, err)
}
return k, 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 ""
}