veyron/veyron/security/agent: Encryption of PEM block in Load/SavePEMKey.

  * This will be used by the agent server to save and load private keys.

Change-Id: I31e7ee239160536b49be0d58cc32291d4933a48d
diff --git a/security/util.go b/security/util.go
index 8af97f2..0ae070f 100644
--- a/security/util.go
+++ b/security/util.go
@@ -3,6 +3,7 @@
 import (
 	"bytes"
 	"crypto/ecdsa"
+	"crypto/rand"
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/json"
@@ -27,9 +28,11 @@
 	return acl
 }
 
-// LoadPEMKey loads a key from 'r', assuming that it was
-// saved using SavePEMKey.
-func LoadPEMKey(r io.Reader) (interface{}, error) {
+// LoadPEMKey loads a key from 'r', assuming that it was saved using SavePEMKey
+// and the specified passphrase 'passphrase'.
+// If passphrase is nil, the key will still be encrypted with a random salt, but
+// this will offer no protection as the salt is stored with the PEMKey.
+func LoadPEMKey(r io.Reader, passphrase []byte) (interface{}, error) {
 	pemKeyBytes, err := ioutil.ReadAll(r)
 	if err != nil {
 		return nil, err
@@ -40,31 +43,39 @@
 		return nil, errors.New("no PEM key block read")
 	}
 
+	data, err := x509.DecryptPEMBlock(pemKey, passphrase)
+	if err != nil {
+		return nil, err
+	}
+
 	switch pemKey.Type {
 	case ecPrivateKeyPEMType:
-		return x509.ParseECPrivateKey(pemKey.Bytes)
+		return x509.ParseECPrivateKey(data)
 	}
 	return nil, fmt.Errorf("PEM key block has an unrecognized type: %v", pemKey.Type)
 }
 
-// SavePEMKey marshals 'key' and saves the bytes to 'w' in PEM format.
+// SavePEMKey marshals 'key', encrypts it using 'passphrase', and saves the bytes to 'w' in PEM format.
 //
 // For example, if key is an ECDSA private key, it will be marshaled
-// in ASN.1, DER format and then written in a PEM block.
-func SavePEMKey(w io.Writer, key interface{}) error {
-	pemKey := &pem.Block{
-		Type: ecPrivateKeyPEMType,
-	}
-
+// 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
 	switch k := key.(type) {
 	case *ecdsa.PrivateKey:
 		var err error
-		if pemKey.Bytes, err = x509.MarshalECPrivateKey(k); err != nil {
+		if data, err = x509.MarshalECPrivateKey(k); err != nil {
 			return err
 		}
 	default:
 		return fmt.Errorf("key of type %T cannot be saved", k)
 	}
+
+	pemKey, err := x509.EncryptPEMBlock(rand.Reader, ecPrivateKeyPEMType, data, passphrase, x509.PEMCipherAES256)
+	if err != nil {
+		return fmt.Errorf("failed to encrypt pem block: %v", err)
+	}
+
 	return pem.Encode(w, pemKey)
 }
 
diff --git a/security/util_test.go b/security/util_test.go
index 0e32e1d..bd55641 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -20,11 +20,41 @@
 	}
 
 	var buf bytes.Buffer
-	if err := SavePEMKey(&buf, key); err != nil {
+	if err := SavePEMKey(&buf, key, nil); err != nil {
 		t.Fatalf("Failed to save ECDSA private key: %v", err)
 	}
 
-	loadedKey, err := LoadPEMKey(&buf)
+	loadedKey, err := LoadPEMKey(&buf, nil)
+	if err != nil {
+		t.Fatalf("Failed to load ECDSA private key: %v", err)
+	}
+	if !reflect.DeepEqual(loadedKey, key) {
+		t.Fatalf("Got key %v, but want %v", loadedKey, key)
+	}
+}
+
+func TestLoadSavePEMKeyWithPassword(t *testing.T) {
+	pass := []byte("openSesame")
+	incorrect_pass := []byte("wrongPassword")
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatalf("Failed ecdsa.GenerateKey: %v", err)
+	}
+	var buf bytes.Buffer
+	// Test incorrect password.
+	if err := SavePEMKey(&buf, key, pass); err != nil {
+		t.Fatalf("Failed to save ECDSA private key: %v", err)
+	}
+	loadedKey, err := LoadPEMKey(&buf, incorrect_pass)
+	if err == nil {
+		t.Errorf("loaded ECDSA private key with incorrect password")
+	}
+
+	// Test correct password.
+	if err := SavePEMKey(&buf, key, pass); err != nil {
+		t.Fatalf("Failed to save ECDSA private key: %v", err)
+	}
+	loadedKey, err = LoadPEMKey(&buf, pass)
 	if err != nil {
 		t.Fatalf("Failed to load ECDSA private key: %v", err)
 	}