blob: 350825ae6f719617fd5c9b544acb23d92cfc6c0d [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Tilak Sharmad6ade0e2014-08-20 16:28:32 -07005package security
6
7import (
Ankur021e38e2014-09-26 10:26:45 -07008 "crypto/ecdsa"
gauthamta134eda2014-11-05 17:57:42 -08009 "crypto/elliptic"
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070010 "crypto/rand"
Ankur021e38e2014-09-26 10:26:45 -070011 "crypto/x509"
Ankur021e38e2014-09-26 10:26:45 -070012 "encoding/pem"
Ankurf044a8d2014-09-05 17:05:24 -070013 "fmt"
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070014 "io"
Ankur021e38e2014-09-26 10:26:45 -070015 "io/ioutil"
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070016
Jiri Simsa6ac95222015-02-23 16:11:49 -080017 "v.io/v23/security"
Mike Burrows7f7088d2015-03-25 13:05:00 -070018 "v.io/v23/verror"
19)
20
21var (
22 // ErrBadPassphrase is a possible return error from LoadPEMKey()
23 ErrBadPassphrase = verror.Register(pkgPath+".errBadPassphrase", verror.NoRetry, "{1:}{2:} passphrase incorrect for decrypting private key{:_}")
24
25 errNoPEMKeyBlock = verror.Register(pkgPath+".errNoPEMKeyBlock", verror.NoRetry, "{1:}{2:} no PEM key block read{:_}")
26 errPEMKeyBlockBadType = verror.Register(pkgPath+".errPEMKeyBlockBadType", verror.NoRetry, "{1:}{2:} PEM key block has an unrecognized type{:_}")
27 errCantSaveKeyType = verror.Register(pkgPath+".errCantSaveKeyType", verror.NoRetry, "{1:}{2:} key of type {3} cannot be saved{:_}")
28 errCantEncryptPEMBlock = verror.Register(pkgPath+".errCantEncryptPEMBlock", verror.NoRetry, "{1:}{2:} failed to encrypt pem block{:_}")
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070029)
30
Ankur021e38e2014-09-26 10:26:45 -070031const ecPrivateKeyPEMType = "EC PRIVATE KEY"
32
gauthamta134eda2014-11-05 17:57:42 -080033// NewPrincipalKey generates an ECDSA (public, private) key pair.
34func NewPrincipalKey() (security.PublicKey, *ecdsa.PrivateKey, error) {
35 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
36 if err != nil {
37 return nil, nil, err
38 }
39 return security.NewECDSAPublicKey(&priv.PublicKey), priv, nil
40}
41
Mike Burrows7f7088d2015-03-25 13:05:00 -070042// LoadPEMKey loads a key from 'r'. returns ErrBadPassphrase for incorrect Passphrase.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070043// If the key held in 'r' is unencrypted, 'passphrase' will be ignored.
Ankur73e7a932014-10-24 15:57:03 -070044func LoadPEMKey(r io.Reader, passphrase []byte) (interface{}, error) {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070045 pemBlockBytes, err := ioutil.ReadAll(r)
Ankur021e38e2014-09-26 10:26:45 -070046 if err != nil {
47 return nil, err
48 }
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070049 pemBlock, _ := pem.Decode(pemBlockBytes)
50 if pemBlock == nil {
Mike Burrows7f7088d2015-03-25 13:05:00 -070051 return nil, verror.New(errNoPEMKeyBlock, nil)
Ankur021e38e2014-09-26 10:26:45 -070052 }
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070053 var data []byte
54 if x509.IsEncryptedPEMBlock(pemBlock) {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070055 data, err = x509.DecryptPEMBlock(pemBlock, passphrase)
56 if err != nil {
Mike Burrows7f7088d2015-03-25 13:05:00 -070057 return nil, verror.New(ErrBadPassphrase, nil)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070058 }
59 } else {
60 data = pemBlock.Bytes
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070061 }
62
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070063 switch pemBlock.Type {
Ankur021e38e2014-09-26 10:26:45 -070064 case ecPrivateKeyPEMType:
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070065 key, err := x509.ParseECPrivateKey(data)
66 if err != nil {
Mike Burrows7f7088d2015-03-25 13:05:00 -070067 return nil, verror.New(ErrBadPassphrase, nil)
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070068 }
69 return key, nil
Ankur021e38e2014-09-26 10:26:45 -070070 }
Mike Burrows7f7088d2015-03-25 13:05:00 -070071 return nil, verror.New(errPEMKeyBlockBadType, nil, pemBlock.Type)
Ankur021e38e2014-09-26 10:26:45 -070072}
73
Ankur73e7a932014-10-24 15:57:03 -070074// SavePEMKey marshals 'key', encrypts it using 'passphrase', and saves the bytes to 'w' in PEM format.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070075// If passphrase is nil, the key will not be encrypted.
Ankur021e38e2014-09-26 10:26:45 -070076//
77// For example, if key is an ECDSA private key, it will be marshaled
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070078// in ASN.1, DER format, encrypted, and then written in a PEM block.
Ankur73e7a932014-10-24 15:57:03 -070079func SavePEMKey(w io.Writer, key interface{}, passphrase []byte) error {
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070080 var data []byte
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070081 var err error
Ankur021e38e2014-09-26 10:26:45 -070082 switch k := key.(type) {
83 case *ecdsa.PrivateKey:
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070084 if data, err = x509.MarshalECPrivateKey(k); err != nil {
Ankur021e38e2014-09-26 10:26:45 -070085 return err
86 }
87 default:
Mike Burrows7f7088d2015-03-25 13:05:00 -070088 return verror.New(errCantSaveKeyType, nil, fmt.Sprintf("%T", k))
Ankur021e38e2014-09-26 10:26:45 -070089 }
Suharsh Sivakumar0f359042014-10-01 22:53:45 -070090
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070091 var pemKey *pem.Block
92 if passphrase != nil {
93 pemKey, err = x509.EncryptPEMBlock(rand.Reader, ecPrivateKeyPEMType, data, passphrase, x509.PEMCipherAES256)
94 if err != nil {
Mike Burrows7f7088d2015-03-25 13:05:00 -070095 return verror.New(errCantEncryptPEMBlock, nil, err)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070096 }
97 } else {
98 pemKey = &pem.Block{
99 Type: ecPrivateKeyPEMType,
100 Bytes: data,
101 }
Suharsh Sivakumar0f359042014-10-01 22:53:45 -0700102 }
103
Ankur021e38e2014-09-26 10:26:45 -0700104 return pem.Encode(w, pemKey)
105}