blob: f7cff3f571046a7467ceba76e3675ccbfa96a87c [file] [log] [blame]
Tilak Sharmad6ade0e2014-08-20 16:28:32 -07001package security
2
3import (
Ankur021e38e2014-09-26 10:26:45 -07004 "crypto/ecdsa"
gauthamta134eda2014-11-05 17:57:42 -08005 "crypto/elliptic"
Suharsh Sivakumar0f359042014-10-01 22:53:45 -07006 "crypto/rand"
Ankur021e38e2014-09-26 10:26:45 -07007 "crypto/x509"
Ankur021e38e2014-09-26 10:26:45 -07008 "encoding/pem"
9 "errors"
Ankurf044a8d2014-09-05 17:05:24 -070010 "fmt"
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070011 "io"
Ankur021e38e2014-09-26 10:26:45 -070012 "io/ioutil"
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070013
Jiri Simsa764efb72014-12-25 20:57:03 -080014 "v.io/core/veyron2/security"
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070015)
16
Ankur021e38e2014-09-26 10:26:45 -070017const ecPrivateKeyPEMType = "EC PRIVATE KEY"
18
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070019var PassphraseErr = errors.New("passphrase incorrect for decrypting private key")
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070020
gauthamta134eda2014-11-05 17:57:42 -080021// NewPrincipalKey generates an ECDSA (public, private) key pair.
22func NewPrincipalKey() (security.PublicKey, *ecdsa.PrivateKey, error) {
23 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
24 if err != nil {
25 return nil, nil, err
26 }
27 return security.NewECDSAPublicKey(&priv.PublicKey), priv, nil
28}
29
Ankur73e7a932014-10-24 15:57:03 -070030// LoadPEMKey loads a key from 'r'. returns PassphraseErr for incorrect Passphrase.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070031// If the key held in 'r' is unencrypted, 'passphrase' will be ignored.
Ankur73e7a932014-10-24 15:57:03 -070032func LoadPEMKey(r io.Reader, passphrase []byte) (interface{}, error) {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070033 pemBlockBytes, err := ioutil.ReadAll(r)
Ankur021e38e2014-09-26 10:26:45 -070034 if err != nil {
35 return nil, err
36 }
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070037 pemBlock, _ := pem.Decode(pemBlockBytes)
38 if pemBlock == nil {
Ankur021e38e2014-09-26 10:26:45 -070039 return nil, errors.New("no PEM key block read")
40 }
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070041 var data []byte
42 if x509.IsEncryptedPEMBlock(pemBlock) {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070043 data, err = x509.DecryptPEMBlock(pemBlock, passphrase)
44 if err != nil {
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070045 return nil, PassphraseErr
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070046 }
47 } else {
48 data = pemBlock.Bytes
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070049 }
50
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070051 switch pemBlock.Type {
Ankur021e38e2014-09-26 10:26:45 -070052 case ecPrivateKeyPEMType:
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070053 key, err := x509.ParseECPrivateKey(data)
54 if err != nil {
55 return nil, PassphraseErr
56 }
57 return key, nil
Ankur021e38e2014-09-26 10:26:45 -070058 }
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070059 return nil, fmt.Errorf("PEM key block has an unrecognized type: %v", pemBlock.Type)
Ankur021e38e2014-09-26 10:26:45 -070060}
61
Ankur73e7a932014-10-24 15:57:03 -070062// SavePEMKey marshals 'key', encrypts it using 'passphrase', and saves the bytes to 'w' in PEM format.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070063// If passphrase is nil, the key will not be encrypted.
Ankur021e38e2014-09-26 10:26:45 -070064//
65// For example, if key is an ECDSA private key, it will be marshaled
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070066// in ASN.1, DER format, encrypted, and then written in a PEM block.
Ankur73e7a932014-10-24 15:57:03 -070067func SavePEMKey(w io.Writer, key interface{}, passphrase []byte) error {
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070068 var data []byte
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070069 var err error
Ankur021e38e2014-09-26 10:26:45 -070070 switch k := key.(type) {
71 case *ecdsa.PrivateKey:
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070072 if data, err = x509.MarshalECPrivateKey(k); err != nil {
Ankur021e38e2014-09-26 10:26:45 -070073 return err
74 }
75 default:
76 return fmt.Errorf("key of type %T cannot be saved", k)
77 }
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070078
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070079 var pemKey *pem.Block
80 if passphrase != nil {
81 pemKey, err = x509.EncryptPEMBlock(rand.Reader, ecPrivateKeyPEMType, data, passphrase, x509.PEMCipherAES256)
82 if err != nil {
83 return fmt.Errorf("failed to encrypt pem block: %v", err)
84 }
85 } else {
86 pemKey = &pem.Block{
87 Type: ecPrivateKeyPEMType,
88 Bytes: data,
89 }
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070090 }
91
Ankur021e38e2014-09-26 10:26:45 -070092 return pem.Encode(w, pemKey)
93}