storj/pkg/pkcrypto/encoding.go
paul cannon c35b93766d
Unite all cryptographic signing and verifying (#1244)
this change removes the cryptopasta dependency.

a couple possible sources of problem with this change:

 * the encoding used for ECDSA signatures on SignedMessage has changed.
   the encoding employed by cryptopasta was workable, but not the same
   as the encoding used for such signatures in the rest of the world
   (most particularly, on ECDSA signatures in X.509 certificates). I
   think we'll be best served by using one ECDSA signature encoding from
   here on, but if we need to use the old encoding for backwards
   compatibility with existing nodes, that can be arranged.

 * since there's already a breaking change in SignedMessage, I changed
   it to send and receive public keys in raw PKIX format, instead of
   PEM. PEM just adds unhelpful overhead for this case.
2019-02-07 14:39:20 -06:00

317 lines
9.2 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pkcrypto
import (
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io"
"math/big"
"github.com/zeebo/errs"
"storj.io/storj/pkg/utils"
)
// WritePublicKeyPEM writes the public key, in a PEM-enveloped
// PKIX form.
func WritePublicKeyPEM(w io.Writer, key crypto.PublicKey) error {
kb, err := PublicKeyToPKIX(key)
if err != nil {
return err
}
err = pem.Encode(w, &pem.Block{Type: BlockLabelPublicKey, Bytes: kb})
return errs.Wrap(err)
}
// PublicKeyToPEM encodes a public key to a PEM-enveloped PKIX form.
func PublicKeyToPEM(key crypto.PublicKey) ([]byte, error) {
kb, err := PublicKeyToPKIX(key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPublicKey, Bytes: kb}), nil
}
// PublicKeyToPKIX serializes a public key to a PKIX-encoded form.
func PublicKeyToPKIX(key crypto.PublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(key)
}
// PublicKeyFromPKIX parses a public key from its PKIX encoding.
func PublicKeyFromPKIX(pkixData []byte) (crypto.PublicKey, error) {
return x509.ParsePKIXPublicKey(pkixData)
}
// PublicKeyFromPEM parses a public key from its PEM-enveloped PKIX
// encoding.
func PublicKeyFromPEM(pemData []byte) (crypto.PublicKey, error) {
pb, _ := pem.Decode(pemData)
if pb == nil {
return nil, ErrParse.New("could not parse PEM encoding")
}
if pb.Type != BlockLabelPublicKey {
return nil, ErrParse.New("can not parse public key from PEM block labeled %q", pb.Type)
}
return PublicKeyFromPKIX(pb.Bytes)
}
// WritePrivateKeyPEM writes the private key to the writer, in a PEM-enveloped
// PKCS#8 form.
func WritePrivateKeyPEM(w io.Writer, key crypto.PrivateKey) error {
kb, err := PrivateKeyToPKCS8(key)
if err != nil {
return errs.Wrap(err)
}
err = pem.Encode(w, &pem.Block{Type: BlockLabelPrivateKey, Bytes: kb})
return errs.Wrap(err)
}
// PrivateKeyToPEM serializes a private key to a PEM-enveloped PKCS#8 form.
func PrivateKeyToPEM(key crypto.PrivateKey) ([]byte, error) {
kb, err := PrivateKeyToPKCS8(key)
if err != nil {
return nil, errs.Wrap(err)
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPrivateKey, Bytes: kb}), nil
}
// PrivateKeyToPKCS8 serializes a private key to a PKCS#8-encoded form.
func PrivateKeyToPKCS8(key crypto.PrivateKey) ([]byte, error) {
return x509.MarshalPKCS8PrivateKey(key)
}
// PrivateKeyFromPKCS8 parses a private key from its PKCS#8 encoding.
func PrivateKeyFromPKCS8(keyBytes []byte) (crypto.PrivateKey, error) {
key, err := x509.ParsePKCS8PrivateKey(keyBytes)
if err != nil {
return nil, err
}
return crypto.PrivateKey(key), nil
}
// PrivateKeyFromPEM parses a private key from its PEM-enveloped PKCS#8
// encoding.
func PrivateKeyFromPEM(keyBytes []byte) (crypto.PrivateKey, error) {
pb, _ := pem.Decode(keyBytes)
if pb == nil {
return nil, ErrParse.New("could not parse PEM encoding")
}
switch pb.Type {
case BlockLabelEcPrivateKey:
return ecPrivateKeyFromASN1(pb.Bytes)
case BlockLabelPrivateKey:
return PrivateKeyFromPKCS8(pb.Bytes)
}
return nil, ErrParse.New("can not parse private key from PEM block labeled %q", pb.Type)
}
// WriteCertPEM writes the certificate to the writer, in a PEM-enveloped DER
// encoding.
func WriteCertPEM(w io.Writer, cert *x509.Certificate) error {
err := pem.Encode(w, &pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
return errs.Wrap(err)
}
// CertToPEM returns the bytes of the certificate, in a PEM-enveloped DER
// encoding.
func CertToPEM(cert *x509.Certificate) []byte {
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
}
// CertToDER returns the bytes of the certificate, in a DER encoding.
//
// Note that this is fairly useless, as x509.Certificate objects are always
// supposed to have a member containing the raw DER encoding. But this is
// included for completeness with the rest of this module's API.
func CertToDER(cert *x509.Certificate) ([]byte, error) {
return cert.Raw, nil
}
// CertFromDER parses an X.509 certificate from its DER encoding.
func CertFromDER(certDER []byte) (*x509.Certificate, error) {
return x509.ParseCertificate(certDER)
}
// CertFromPEM parses an X.509 certificate from its PEM-enveloped DER encoding.
func CertFromPEM(certPEM []byte) (*x509.Certificate, error) {
kb, _ := pem.Decode(certPEM)
if kb == nil {
return nil, ErrParse.New("could not decode certificate as PEM")
}
if kb.Type != BlockLabelCertificate {
return nil, ErrParse.New("can not parse certificate from PEM block labeled %q", kb.Type)
}
return CertFromDER(kb.Bytes)
}
// CertsFromDER parses an x509 certificate from each of the given byte
// slices, which should be encoded in DER.
func CertsFromDER(rawCerts [][]byte) ([]*x509.Certificate, error) {
certs := make([]*x509.Certificate, len(rawCerts))
for i, c := range rawCerts {
var err error
certs[i], err = CertFromDER(c)
if err != nil {
return nil, ErrParse.New("unable to parse certificate at index %d", i)
}
}
return certs, nil
}
// CertsFromPEM parses a PEM chain from a single byte string (the PEM-enveloped
// certificates should be concatenated). The PEM blocks may include PKIX
// extensions.
func CertsFromPEM(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 BlockLabelCertificate:
encChain.AddCert(pemBlock.Bytes)
case BlockLabelExtension:
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
blockErrs.Add(err)
}
}
}
if err := blockErrs.Finish(); err != nil {
return nil, err
}
return encChain.Parse()
}
type encodedChain struct {
chain [][]byte
extensions [][][]byte
}
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 := CertsFromDER(e.chain)
if err != nil {
return nil, err
}
var extErrs utils.ErrorGroup
for i, cert := range chain {
for _, ee := range e.extensions[i] {
ext, err := PKIXExtensionFromASN1(ee)
if err != nil {
extErrs.Add(err)
}
cert.ExtraExtensions = append(cert.ExtraExtensions, *ext)
}
}
if err := extErrs.Finish(); err != nil {
return nil, err
}
return chain, nil
}
// WritePKIXExtensionPEM writes the certificate extension to the writer, in a PEM-
// enveloped PKIX form.
func WritePKIXExtensionPEM(w io.Writer, extension *pkix.Extension) error {
extBytes, err := PKIXExtensionToASN1(extension)
if err != nil {
return errs.Wrap(err)
}
err = pem.Encode(w, &pem.Block{Type: BlockLabelExtension, Bytes: extBytes})
return errs.Wrap(err)
}
// PKIXExtensionToPEM serializes a PKIX certificate extension to PEM-
// enveloped ASN.1 bytes.
func PKIXExtensionToPEM(extension *pkix.Extension) ([]byte, error) {
asn, err := PKIXExtensionToASN1(extension)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelExtension, Bytes: asn}), nil
}
// PKIXExtensionToASN1 serializes a PKIX certificate extension to the
// appropriate ASN.1 structure for such things. See RFC 5280, section 4.1.1.2.
func PKIXExtensionToASN1(extension *pkix.Extension) ([]byte, error) {
extBytes, err := asn1.Marshal(extension)
return extBytes, errs.Wrap(err)
}
// PKIXExtensionFromASN1 deserializes a PKIX certificate extension from
// the appropriate ASN.1 structure for such things.
func PKIXExtensionFromASN1(extData []byte) (*pkix.Extension, error) {
var extension pkix.Extension
if _, err := asn1.Unmarshal(extData, &extension); err != nil {
return nil, ErrParse.New("unable to unmarshal PKIX extension: %v", err)
}
return &extension, nil
}
// PKIXExtensionFromPEM parses a PKIX certificate extension from
// PEM-enveloped ASN.1 bytes.
func PKIXExtensionFromPEM(pemBytes []byte) (*pkix.Extension, error) {
pb, _ := pem.Decode(pemBytes)
if pb == nil {
return nil, ErrParse.New("unable to parse PEM block")
}
if pb.Type != BlockLabelExtension {
return nil, ErrParse.New("can not parse PKIX cert extension from PEM block labeled %q", pb.Type)
}
return PKIXExtensionFromASN1(pb.Bytes)
}
type ecdsaSignature struct {
R, S *big.Int
}
func marshalECDSASignature(r, s *big.Int) ([]byte, error) {
return asn1.Marshal(ecdsaSignature{R: r, S: s})
}
func unmarshalECDSASignature(signatureBytes []byte) (r, s *big.Int, err error) {
var signature ecdsaSignature
if _, err = asn1.Unmarshal(signatureBytes, &signature); err != nil {
return nil, nil, err
}
return signature.R, signature.S, nil
}
// ecPrivateKeyFromASN1 parses a private key from the special Elliptic Curve
// Private Key ASN.1 structure. This is here only for backward compatibility.
// Use PKCS#8 instead.
func ecPrivateKeyFromASN1(privKeyData []byte) (crypto.PrivateKey, error) {
key, err := x509.ParseECPrivateKey(privKeyData)
if err != nil {
return nil, err
}
return crypto.PrivateKey(key), nil
}