blob: f3cbb782fb7265972f99faf1c22940b67ca2a181 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package security
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"v.io/v23/security"
"v.io/v23/verror"
)
var (
// ErrBadPassphrase is a possible return error from LoadPEMKey()
ErrBadPassphrase = verror.Register(pkgPath+".errBadPassphrase", verror.NoRetry, "{1:}{2:} passphrase incorrect for decrypting private key{:_}")
// ErrPassphraseRequired is a possible return error from LoadPEMKey()
ErrPassphraseRequired = verror.Register(pkgPath+".errPassphraseRequired", verror.NoRetry, "{1:}{2:} passphrase required for decrypting private key{:_}")
errNoPEMKeyBlock = verror.Register(pkgPath+".errNoPEMKeyBlock", verror.NoRetry, "{1:}{2:} no PEM key block read{:_}")
errPEMKeyBlockBadType = verror.Register(pkgPath+".errPEMKeyBlockBadType", verror.NoRetry, "{1:}{2:} PEM key block has an unrecognized type{:_}")
errCantSaveKeyType = verror.Register(pkgPath+".errCantSaveKeyType", verror.NoRetry, "{1:}{2:} key of type {3} cannot be saved{:_}")
errCantEncryptPEMBlock = verror.Register(pkgPath+".errCantEncryptPEMBlock", verror.NoRetry, "{1:}{2:} failed to encrypt pem block{:_}")
)
const ecPrivateKeyPEMType = "EC PRIVATE KEY"
// NewPrincipalKey generates an ECDSA (public, private) key pair.
func NewPrincipalKey() (security.PublicKey, *ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
return security.NewECDSAPublicKey(&priv.PublicKey), priv, nil
}
// LoadPEMKey loads a key from 'r'. returns ErrBadPassphrase for incorrect Passphrase.
// If the key held in 'r' is unencrypted, 'passphrase' will be ignored.
func LoadPEMKey(r io.Reader, passphrase []byte) (interface{}, error) {
pemBlockBytes, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
pemBlock, _ := pem.Decode(pemBlockBytes)
if pemBlock == nil {
return nil, verror.New(errNoPEMKeyBlock, nil)
}
var data []byte
if x509.IsEncryptedPEMBlock(pemBlock) {
// Assume empty passphrase is disallowed.
if len(passphrase) == 0 {
return nil, verror.New(ErrPassphraseRequired, nil)
}
data, err = x509.DecryptPEMBlock(pemBlock, passphrase)
if err != nil {
return nil, verror.New(ErrBadPassphrase, nil)
}
} else {
data = pemBlock.Bytes
}
switch pemBlock.Type {
case ecPrivateKeyPEMType:
key, err := x509.ParseECPrivateKey(data)
if err != nil {
// x509.DecryptPEMBlock may occasionally return random
// bytes for data with a nil error when the passphrase
// is invalid; hence, failure to parse data could be due
// to a bad passphrase.
return nil, verror.New(ErrBadPassphrase, nil)
}
return key, nil
}
return nil, verror.New(errPEMKeyBlockBadType, nil, pemBlock.Type)
}
// SavePEMKey marshals 'key', encrypts it using 'passphrase', and saves the bytes to 'w' in PEM format.
// If passphrase is nil, the key will not be encrypted.
//
// For example, if key is an ECDSA private key, it will be marshaled
// in ASN.1, DER format, encrypted, and then written in a PEM block.
func SavePEMKey(w io.Writer, key interface{}, passphrase []byte) error {
var data []byte
var err error
switch k := key.(type) {
case *ecdsa.PrivateKey:
if data, err = x509.MarshalECPrivateKey(k); err != nil {
return err
}
default:
return verror.New(errCantSaveKeyType, nil, fmt.Sprintf("%T", k))
}
var pemKey *pem.Block
if passphrase != nil {
pemKey, err = x509.EncryptPEMBlock(rand.Reader, ecPrivateKeyPEMType, data, passphrase, x509.PEMCipherAES256)
if err != nil {
return verror.New(errCantEncryptPEMBlock, nil, err)
}
} else {
pemKey = &pem.Block{
Type: ecPrivateKeyPEMType,
Bytes: data,
}
}
return pem.Encode(w, pemKey)
}