Add ECC support to passvault.

This adds code to handle ECC records; the RSA code remains intact.
While old password records are not affected, new records use ECC. Due to
the use of public keys to encrypt a KEK that is then used to encrypt
data, there is no visible change to end users. A user with an RSA key
can interact and share a secret with a user who has an ECC key.

The bulk of this commit simply adds cases for handling EC records.
This commit is contained in:
Kyle
2014-01-09 19:18:16 -07:00
parent 689f0e6fdc
commit 5ea44c0ffb
2 changed files with 80 additions and 13 deletions

View File

@@ -20,6 +20,7 @@ import (
"encoding/binary"
"encoding/json"
"errors"
"github.com/cloudflare/redoctober/ecdh"
"github.com/cloudflare/redoctober/padding"
"io/ioutil"
"math/big"
@@ -34,6 +35,8 @@ const (
ECCRecord = "ECC"
)
var DefaultRecordType = ECCRecord
// Constants for scrypt
const (
KEYLENGTH = 16 // 16-byte output from scrypt
@@ -164,7 +167,7 @@ func encryptECCRecord(newRec *PasswordRecord, ecPriv *ecdsa.PrivateKey, passKey
// createPasswordRec creates a new record from a username and password
func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err error) {
newRec.Type = ECCRecord
newRec.Type = DefaultRecordType
if newRec.PasswordSalt, err = makeRandom(16); err != nil {
return
@@ -184,18 +187,31 @@ func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err
}
// generate a key pair
ecPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return
switch DefaultRecordType {
case RSARecord:
var rsaPriv *rsa.PrivateKey
rsaPriv, err = rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return
}
// encrypt RSA key with password key
if err = encryptRSARecord(&newRec, rsaPriv, passKey); err != nil {
return
}
newRec.RSAKey.RSAPublic = rsaPriv.PublicKey
case ECCRecord:
var ecPriv *ecdsa.PrivateKey
ecPriv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return
}
// encrypt ECDSA key with password key
if err = encryptECCRecord(&newRec, ecPriv, passKey); err != nil {
return
}
newRec.ECKey.ECPublic = ecPriv.PublicKey
}
// encrypt RSA key with password key
if err = encryptECCRecord(&newRec, ecPriv, passKey); err != nil {
return
}
newRec.ECKey.ECPublic = ecPriv.PublicKey
// encrypt AES key with password key
aesKey, err := makeRandom(16)
if err != nil {
@@ -331,6 +347,11 @@ func InitFromDisk(path string) error {
return formatErr
}
}
if rec.Type == ECCRecord {
if len(rec.ECKey.ECPriv) == 0 {
return formatErr
}
}
}
// If the Version field is 0 then it indicates that nothing was
@@ -388,6 +409,7 @@ func ChangePassword(name, password, newPassword string) (err error) {
// decrypt key
var key []byte
var rsaKey rsa.PrivateKey
var ecKey *ecdsa.PrivateKey
if pr.Type == AESRecord {
key, err = pr.GetKeyAES(password)
if err != nil {
@@ -398,6 +420,11 @@ func ChangePassword(name, password, newPassword string) (err error) {
if err != nil {
return
}
} else if pr.Type == ECCRecord {
ecKey, err = pr.GetKeyECC(password)
if err != nil {
return
}
} else {
err = errors.New("Unkown record type")
return
@@ -431,6 +458,12 @@ func ChangePassword(name, password, newPassword string) (err error) {
if err != nil {
return
}
} else if pr.Type == ECCRecord {
// encrypt ECDSA key with password key
err = encryptECCRecord(&pr, ecKey, newPassKey)
if err != nil {
return
}
} else {
err = errors.New("Unkown record type")
return
@@ -531,9 +564,15 @@ func (pr PasswordRecord) GetType() string {
return pr.Type
}
// EncryptKey encrypts a 16-byte key with the RSA key of the record.
// EncryptKey encrypts a 16-byte key with the RSA or EC key of the record.
func (pr PasswordRecord) EncryptKey(in []byte) (out []byte, err error) {
return rsa.EncryptOAEP(sha1.New(), rand.Reader, &pr.RSAKey.RSAPublic, in, nil)
if pr.Type == RSARecord {
return rsa.EncryptOAEP(sha1.New(), rand.Reader, &pr.RSAKey.RSAPublic, in, nil)
} else if pr.Type == ECCRecord {
return ecdh.Encrypt(pr.ECKey.ECPublic, in)
} else {
return nil, errors.New("Invalid function for record type")
}
}
// GetKeyAES returns the 16-byte key of the record.

View File

@@ -9,6 +9,8 @@ import (
)
func TestRSAEncryptDecrypt(t *testing.T) {
oldDefaultRecordType := DefaultRecordType
DefaultRecordType = RSARecord
myRec, err := createPasswordRec("mypasswordisweak", true)
if err != nil {
t.Fatalf("Error creating record")
@@ -33,4 +35,30 @@ func TestRSAEncryptDecrypt(t *testing.T) {
if err != nil {
t.Fatalf("Error validating RSA key")
}
DefaultRecordType = oldDefaultRecordType
}
func TestECCEncryptDecrypt(t *testing.T) {
oldDefaultRecordType := DefaultRecordType
DefaultRecordType = ECCRecord
myRec, err := createPasswordRec("mypasswordisweak", true)
if err != nil {
t.Fatalf("Error creating record")
}
_, err = myRec.GetKeyECCPub()
if err != nil {
t.Fatalf("Error extracting EC pub")
}
_, err = myRec.GetKeyECC("mypasswordiswrong")
if err == nil {
t.Fatalf("Incorrect password did not fail")
}
_, err = myRec.GetKeyECC("mypasswordisweak")
if err != nil {
t.Fatalf("Error decrypting EC key")
}
DefaultRecordType = oldDefaultRecordType
}