diff --git a/core/core.go b/core/core.go
index 1aac20e..b182026 100644
--- a/core/core.go
+++ b/core/core.go
@@ -15,6 +15,12 @@ import (
"github.com/cloudflare/redoctober/passvault"
)
+var (
+ crypt cryptor.Cryptor
+ records passvault.Records
+ cache keycache.Cache
+)
+
// Each of these structures corresponds to the JSON expected on the
// correspondingly named URI (e.g. the delegate structure maps to the
// JSON that should be sent on the /delegate URI and it is handled by
@@ -51,11 +57,11 @@ type EncryptRequest struct {
Name string
Password string
- Minimum int
Owners []string
LeftOwners []string
RightOwners []string
- Data []byte
+
+ Data []byte
Labels []string
}
@@ -90,6 +96,7 @@ type SummaryData struct {
type DecryptWithDelegates struct {
Data []byte
+ Secure bool
Delegates []string
}
@@ -102,7 +109,7 @@ func jsonStatusError(err error) ([]byte, error) {
return json.Marshal(ResponseData{Status: err.Error()})
}
func jsonSummary() ([]byte, error) {
- return json.Marshal(SummaryData{Status: "ok", Live: keycache.GetSummary(), All: passvault.GetSummary()})
+ return json.Marshal(SummaryData{Status: "ok", Live: cache.GetSummary(), All: records.GetSummary()})
}
func jsonResponse(resp []byte) ([]byte, error) {
return json.Marshal(ResponseData{Status: "ok", Response: resp})
@@ -111,11 +118,11 @@ func jsonResponse(resp []byte) ([]byte, error) {
// validateAdmin checks that the username and password passed in are
// correct and that the user is an admin
func validateAdmin(name, password string) error {
- if passvault.NumRecords() == 0 {
+ if records.NumRecords() == 0 {
return errors.New("Vault is not created yet")
}
- pr, ok := passvault.GetRecord(name)
+ pr, ok := records.GetRecord(name)
if !ok {
return errors.New("User not present")
}
@@ -144,9 +151,13 @@ func validateUser(name, password string) error {
// Init reads the records from disk from a given path
func Init(path string) (err error) {
- if err = passvault.InitFromDisk(path); err != nil {
+ if records, err = passvault.InitFrom(path); err != nil {
err = fmt.Errorf("Failed to load password vault %s: %s", path, err)
}
+
+ cache = keycache.Cache{make(map[string]keycache.ActiveUser)}
+ crypt = cryptor.New(&records, &cache)
+
return
}
@@ -157,7 +168,7 @@ func Create(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if passvault.NumRecords() != 0 {
+ if records.NumRecords() != 0 {
return jsonStatusError(errors.New("Vault is already created"))
}
@@ -166,7 +177,7 @@ func Create(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if _, err := passvault.AddNewRecord(s.Name, s.Password, true); err != nil {
+ if _, err := records.AddNewRecord(s.Name, s.Password, true, passvault.DefaultRecordType); err != nil {
log.Printf("Error adding record for %s: %s\n", s.Name, err)
return jsonStatusError(err)
}
@@ -177,13 +188,13 @@ func Create(jsonIn []byte) ([]byte, error) {
// Summary processes a summary request.
func Summary(jsonIn []byte) ([]byte, error) {
var s SummaryRequest
- keycache.Refresh()
+ cache.Refresh()
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
- if passvault.NumRecords() == 0 {
+ if records.NumRecords() == 0 {
return jsonStatusError(errors.New("Vault is not created yet"))
}
@@ -202,7 +213,7 @@ func Delegate(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if passvault.NumRecords() == 0 {
+ if records.NumRecords() == 0 {
return jsonStatusError(errors.New("Vault is not created yet"))
}
@@ -214,21 +225,21 @@ func Delegate(jsonIn []byte) ([]byte, error) {
// Find password record for user and verify that their password
// matches. If not found then add a new entry for this user.
- pr, found := passvault.GetRecord(s.Name)
+ pr, found := records.GetRecord(s.Name)
if found {
if err := pr.ValidatePassword(s.Password); err != nil {
return jsonStatusError(err)
}
} else {
var err error
- if pr, err = passvault.AddNewRecord(s.Name, s.Password, false); err != nil {
+ if pr, err = records.AddNewRecord(s.Name, s.Password, false, passvault.DefaultRecordType); err != nil {
log.Printf("Error adding record for %s: %s\n", s.Name, err)
return jsonStatusError(err)
}
}
// add signed-in record to active set
- if err := keycache.AddKeyFromRecord(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Time); err != nil {
+ if err := cache.AddKeyFromRecord(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Time); err != nil {
log.Printf("Error adding key to cache for %s: %s\n", s.Name, err)
return jsonStatusError(err)
}
@@ -243,12 +254,12 @@ func Password(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if passvault.NumRecords() == 0 {
+ if records.NumRecords() == 0 {
return jsonStatusError(errors.New("Vault is not created yet"))
}
// add signed-in record to active set
- if err := passvault.ChangePassword(s.Name, s.Password, s.NewPassword); err != nil {
+ if err := records.ChangePassword(s.Name, s.Password, s.NewPassword); err != nil {
log.Println("Error changing password:", err)
return jsonStatusError(err)
}
@@ -268,13 +279,9 @@ func Encrypt(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if len(s.Owners) > 0 {
- s.LeftOwners = s.Owners
- s.RightOwners = s.Owners
- }
-
- // Encrypt file with list of owners
- if resp, err := cryptor.Encrypt(s.Data, s.Labels, s.LeftOwners, s.RightOwners, s.Minimum); err != nil {
+ // Encrypt file
+ access := cryptor.AccessStructure{s.Owners, s.LeftOwners, s.RightOwners}
+ if resp, err := crypt.Encrypt(s.Data, s.Labels, access); err != nil {
log.Println("Error encrypting:", err)
return jsonStatusError(err)
} else {
@@ -296,7 +303,7 @@ func Decrypt(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- data, names, err := cryptor.Decrypt(s.Data, s.Name)
+ data, names, secure, err := crypt.Decrypt(s.Data, s.Name)
if err != nil {
log.Println("Error decrypting:", err)
return jsonStatusError(err)
@@ -304,6 +311,7 @@ func Decrypt(jsonIn []byte) ([]byte, error) {
resp := &DecryptWithDelegates{
Data: data,
+ Secure: secure,
Delegates: names,
}
@@ -328,7 +336,7 @@ func Modify(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
- if _, ok := passvault.GetRecord(s.ToModify); !ok {
+ if _, ok := records.GetRecord(s.ToModify); !ok {
return jsonStatusError(errors.New("Record to modify missing"))
}
@@ -339,11 +347,11 @@ func Modify(jsonIn []byte) ([]byte, error) {
var err error
switch s.Command {
case "delete":
- err = passvault.DeleteRecord(s.ToModify)
+ err = records.DeleteRecord(s.ToModify)
case "revoke":
- err = passvault.RevokeRecord(s.ToModify)
+ err = records.RevokeRecord(s.ToModify)
case "admin":
- err = passvault.MakeAdmin(s.ToModify)
+ err = records.MakeAdmin(s.ToModify)
default:
return jsonStatusError(errors.New("Unknown command"))
}
diff --git a/core/core_test.go b/core/core_test.go
index 4acadcc..688c8b6 100644
--- a/core/core_test.go
+++ b/core/core_test.go
@@ -10,15 +10,13 @@ import (
"os"
"testing"
- "github.com/cloudflare/redoctober/keycache"
"github.com/cloudflare/redoctober/passvault"
)
func TestCreate(t *testing.T) {
createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}")
- os.Remove("/tmp/db1.json")
- Init("/tmp/db1.json")
+ Init("memory")
respJson, err := Create(createJson)
if err != nil {
@@ -46,14 +44,11 @@ func TestCreate(t *testing.T) {
if s.Status == "ok" {
t.Fatalf("Error in creating account when one exists, %v", s.Status)
}
-
- os.Remove("/tmp/db1.json")
}
func TestSummary(t *testing.T) {
createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}")
delegateJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Rob\",\"Time\":\"2h\",\"Uses\":1}")
- os.Remove("/tmp/db1.json")
// check for summary of uninitialized vault
respJson, err := Summary(createJson)
@@ -69,7 +64,7 @@ func TestSummary(t *testing.T) {
t.Fatalf("Error in summary of account with no vault, %v", s.Status)
}
- Init("/tmp/db1.json")
+ Init("memory")
// check for summary of initialized vault
respJson, err = Create(createJson)
@@ -156,7 +151,7 @@ func TestSummary(t *testing.T) {
dataLive, ok := s.Live["Bob"]
if !ok {
- t.Fatalf("Error in summary of account, record missing, %v", keycache.UserKeys)
+ t.Fatalf("Error in summary of account, record missing, %v", cache.UserKeys)
}
if dataLive.Admin != false {
t.Fatalf("Error in summary of account, record missing")
@@ -164,10 +159,6 @@ func TestSummary(t *testing.T) {
if dataLive.Type != passvault.DefaultRecordType {
t.Fatalf("Error in summary of account, record missing")
}
-
- keycache.FlushCache()
-
- os.Remove("/tmp/db1.json")
}
func TestPassword(t *testing.T) {
@@ -176,9 +167,8 @@ func TestPassword(t *testing.T) {
passwordJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"NewPassword\":\"Olleh\"}")
delegateJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Olleh\",\"Time\":\"2h\",\"Uses\":1}")
passwordJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Olleh\",\"NewPassword\":\"Hello\"}")
- os.Remove("/tmp/db1.json")
- Init("/tmp/db1.json")
+ Init("memory")
// check for summary of initialized vault with new member
var s ResponseData
@@ -277,10 +267,6 @@ func TestPassword(t *testing.T) {
if s.Status != "ok" {
t.Fatalf("Error in delegating account, %v", s.Status)
}
-
- keycache.FlushCache()
-
- os.Remove("/tmp/db1.json")
}
func TestEncryptDecrypt(t *testing.T) {
@@ -292,9 +278,8 @@ func TestEncryptDecrypt(t *testing.T) {
delegateJson5 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":2,\"Users\":[\"Alice\"],\"Labels\":[\"blue\"]}")
encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minumum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}")
encryptJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Minumum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\",\"Labels\":[\"blue\",\"red\"]}")
- os.Remove("/tmp/db1.json")
- Init("/tmp/db1.json")
+ Init("memory")
// check for summary of initialized vault with new member
var s ResponseData
@@ -335,7 +320,7 @@ func TestEncryptDecrypt(t *testing.T) {
}
// check summary to see if none are delegated
- keycache.Refresh()
+ cache.Refresh()
respJson, err = Summary(summaryJson)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -422,7 +407,7 @@ func TestEncryptDecrypt(t *testing.T) {
}
// verify the presence of the two delgations
- keycache.Refresh()
+ cache.Refresh()
var sum2 SummaryData
respJson, err = Summary(summaryJson)
if err != nil {
@@ -466,10 +451,6 @@ func TestEncryptDecrypt(t *testing.T) {
t.Fatalf("Error in decrypt, %v", d.Delegates)
}
}
-
- keycache.FlushCache()
-
- os.Remove("/tmp/db1.json")
}
func TestModify(t *testing.T) {
@@ -484,8 +465,7 @@ func TestModify(t *testing.T) {
modifyJson4 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"ToModify\":\"Alice\",\"Command\":\"revoke\"}")
modifyJson5 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"ToModify\":\"Alice\",\"Command\":\"delete\"}")
- os.Remove("/tmp/db1.json")
- Init("/tmp/db1.json")
+ Init("memory")
// check for summary of initialized vault with new member
var s ResponseData
@@ -526,7 +506,7 @@ func TestModify(t *testing.T) {
}
// check summary to see if none are delegated
- keycache.Refresh()
+ cache.Refresh()
respJson, err = Summary(summaryJson)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -653,10 +633,6 @@ func TestModify(t *testing.T) {
if len(sum3.All) != 2 {
t.Fatalf("Error in summary, %v", sum3.All)
}
-
- keycache.FlushCache()
-
- os.Remove("/tmp/db1.json")
}
func TestStatic(t *testing.T) {
@@ -731,7 +707,7 @@ func TestStatic(t *testing.T) {
t.Fatalf("Error in summary, %v, %v", expected, r.Response)
}
- keycache.FlushCache()
+ cache.FlushCache()
os.Remove("/tmp/db1.json")
}
diff --git a/cryptor/cryptor.go b/cryptor/cryptor.go
index ecb4613..0d27cd5 100644
--- a/cryptor/cryptor.go
+++ b/cryptor/cryptor.go
@@ -25,6 +25,27 @@ const (
DEFAULT_VERSION = 1
)
+type Cryptor struct {
+ records *passvault.Records
+ cache *keycache.Cache
+}
+
+func New(records *passvault.Records, cache *keycache.Cache) Cryptor {
+ return Cryptor{records, cache}
+}
+
+// AccessStructure represents different possible access structures for
+// encrypted data. If len(Names) > 0, then at least 2 of the users in the list
+// must be delegated to decrypt. If len(LeftNames) > 0 & len(RightNames) > 0,
+// then at least one from each list must be delegated (if the same user is in
+// both, then he can decrypt it alone).
+type AccessStructure struct {
+ Names []string
+
+ LeftNames []string
+ RightNames []string
+}
+
// MultiWrappedKey is a structure containing a 16-byte key encrypted
// once for each of the keys corresponding to the names of the users
// in Name in order.
@@ -44,185 +65,56 @@ type SingleWrappedKey struct {
// keys necessary to decrypt it when delegated.
type EncryptedData struct {
Version int
- VaultId int
- Labels []string
- KeySet []MultiWrappedKey
- KeySetRSA map[string]SingleWrappedKey
- IV []byte
+ VaultId int `json:",omitempty"`
+ Labels []string `json:",omitempty"`
+ KeySet []MultiWrappedKey `json:",omitempty"`
+ KeySetRSA map[string]SingleWrappedKey `json:",omitempty"`
+ IV []byte `json:",omitempty"`
Data []byte
Signature []byte
}
-// encryptKey encrypts data with the key associated with name inner,
-// then name outer
-func encryptKey(nameInner, nameOuter string, clearKey []byte, pubKeys map[string]SingleWrappedKey) (out MultiWrappedKey, err error) {
- out.Name = []string{nameOuter, nameInner}
-
- recInner, ok := passvault.GetRecord(nameInner)
- if !ok {
- err = errors.New("Missing user on disk")
- return
- }
-
- recOuter, ok := passvault.GetRecord(nameOuter)
- if !ok {
- err = errors.New("Missing user on disk")
- return
- }
-
- if recInner.Type != recOuter.Type {
- err = errors.New("Mismatched record types")
- return
- }
-
- var keyBytes []byte
- var overrideInner SingleWrappedKey
- var overrideOuter SingleWrappedKey
-
- // For AES records, use the live user key
- // For RSA and ECC records, use the public key from the passvault
- switch recInner.Type {
- case passvault.RSARecord, passvault.ECCRecord:
- if overrideInner, ok = pubKeys[nameInner]; !ok {
- err = errors.New("Missing user in file")
- return
- }
-
- if overrideOuter, ok = pubKeys[nameOuter]; !ok {
- err = errors.New("Missing user in file")
- return
- }
- case passvault.AESRecord:
- break
-
- default:
- return out, errors.New("Unknown record type inner")
- }
-
- // double-wrap the keys
- if keyBytes, err = keycache.EncryptKey(clearKey, nameInner, overrideInner.aesKey); err != nil {
- return out, err
- }
- if keyBytes, err = keycache.EncryptKey(keyBytes, nameOuter, overrideOuter.aesKey); err != nil {
- return out, err
- }
-
- out.Key = keyBytes
-
- return
+type pair struct {
+ name string
+ key []byte
}
-// unwrapKey decrypts first key in keys whose encryption keys are in keycache
-func unwrapKey(keys []MultiWrappedKey, pubKeys map[string]SingleWrappedKey, user string, labels []string) (unwrappedKey []byte, names []string, err error) {
- var (
- keyFound error
- fullMatch bool = false
- nameSet = map[string]bool{}
- )
+type mwkSlice []MultiWrappedKey
+type swkSlice []pair
- for _, mwKey := range keys {
- if err != nil {
- return nil, nil, err
- }
-
- tmpKeyValue := mwKey.Key
-
- for _, mwName := range mwKey.Name {
- pubEncrypted := pubKeys[mwName]
- // if this is null, it's an AES encrypted key
- if tmpKeyValue, keyFound = keycache.DecryptKey(tmpKeyValue, mwName, user, labels, pubEncrypted.Key); keyFound != nil {
- break
- }
- nameSet[mwName] = true
- }
- if keyFound == nil {
- fullMatch = true
- // concatenate all the decrypted bytes
- unwrappedKey = tmpKeyValue
- break
- }
- }
-
- if !fullMatch {
- err = errors.New("Need more delegated keys")
- names = nil
- }
-
- names = make([]string, 0, len(nameSet))
- for name := range nameSet {
- names = append(names, name)
- }
- return
-}
-
-// mwkSorter describes a slice of MultiWrappedKeys to be sorted.
-type mwkSorter struct {
- keySet []MultiWrappedKey
-}
-
-// Len is part of sort.Interface.
-func (s *mwkSorter) Len() int {
- return len(s.keySet)
-}
-
-// Swap is part of sort.Interface.
-func (s *mwkSorter) Swap(i, j int) {
- s.keySet[i], s.keySet[j] = s.keySet[j], s.keySet[i]
-}
-
-// Less is part of sort.Interface, it sorts lexicographically
-// based on the list of names
-func (s *mwkSorter) Less(i, j int) bool {
+func (s mwkSlice) Len() int { return len(s) }
+func (s mwkSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s mwkSlice) Less(i, j int) bool { // Alphabetic order
var shorter = i
- if len(s.keySet[i].Name) > len(s.keySet[j].Name) {
+ if len(s[i].Name) > len(s[j].Name) {
shorter = j
}
- for index := range s.keySet[shorter].Name {
- if s.keySet[i].Name[index] != s.keySet[j].Name[index] {
- return s.keySet[i].Name[index] < s.keySet[j].Name[index]
+
+ for index := range s[shorter].Name {
+ if s[i].Name[index] != s[j].Name[index] {
+ return s[i].Name[index] < s[j].Name[index]
}
}
return false
}
-// swkSorter joins a slice of names with SingleWrappedKeys to be sorted.
-type pair struct {
- name string
- key []byte
-}
-
-type swkSorter []pair
-
-// Len is part of sort.Interface.
-func (s swkSorter) Len() int {
- return len(s)
-}
-
-// Swap is part of sort.Interface.
-func (s swkSorter) Swap(i, j int) {
- s[i], s[j] = s[j], s[i]
-}
-
-// Less is part of sort.Interface.
-func (s swkSorter) Less(i, j int) bool {
- return s[i].name < s[j].name
-}
+func (s swkSlice) Len() int { return len(s) }
+func (s swkSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s swkSlice) Less(i, j int) bool { return s[i].name < s[j].name }
// computeHmac computes the signature of the encrypted data structure
// the signature takes into account every element of the EncryptedData
// structure, with all keys sorted alphabetically by name
-func computeHmac(key []byte, encrypted EncryptedData) []byte {
+func (encrypted *EncryptedData) computeHmac(key []byte) []byte {
mac := hmac.New(sha1.New, key)
// sort the multi-wrapped keys
- mwks := &mwkSorter{
- keySet: encrypted.KeySet,
- }
+ mwks := mwkSlice(encrypted.KeySet)
sort.Sort(mwks)
// sort the singly-wrapped keys
- var swks swkSorter
+ var swks swkSlice
for name, val := range encrypted.KeySetRSA {
swks = append(swks, pair{name, val.Key})
}
@@ -261,97 +153,229 @@ func computeHmac(key []byte, encrypted EncryptedData) []byte {
return mac.Sum(nil)
}
-// Encrypt encrypts data with the keys associated with names. This
-// requires a minimum of min keys to decrypt. NOTE: as currently
-// implemented, the maximum value for min is 2.
-func Encrypt(in []byte, labels, leftNames, rightNames []string, min int) (resp []byte, err error) {
- if min > 2 {
- return nil, errors.New("Minimum restricted to 2")
- }
-
- var encrypted EncryptedData
- encrypted.Version = DEFAULT_VERSION
- if encrypted.VaultId, err = passvault.GetVaultId(); err != nil {
- return
- }
-
- // Generate random IV and encryption key
- ivBytes, err := symcrypt.MakeRandom(16)
+func (encrypted *EncryptedData) lock(key []byte) (err error) {
+ payload, err := json.Marshal(encrypted)
if err != nil {
return
}
- // append used here to make a new slice from ivBytes and assign to
- // encrypted.IV
+ mac := hmac.New(sha1.New, key)
+ mac.Write(payload)
+ sig := mac.Sum(nil)
- encrypted.IV = append([]byte{}, ivBytes...)
- clearKey, err := symcrypt.MakeRandom(16)
- if err != nil {
+ *encrypted = EncryptedData{
+ Version: -1,
+ Data: payload,
+ Signature: sig,
+ }
+
+ return
+}
+
+func (encrypted *EncryptedData) unlock(key []byte) (err error) {
+ if encrypted.Version != -1 {
return
}
- var names = make(map[string]bool)
- var overlap int
+ mac := hmac.New(sha1.New, key)
+ mac.Write(encrypted.Data)
+ sig := mac.Sum(nil)
- // Count overlapping names, we don't want to double-encrypt
- // with the same name
- for _, n := range leftNames {
- names[n] = true
- }
- for _, n := range rightNames {
- used, ok := names[n]
- if !used && ok {
- names[n] = true
- } else {
- overlap++
- }
+ if !hmac.Equal(encrypted.Signature, sig) {
+ err = errors.New("Signature mismatch")
+ return
}
- // Allocate set of keys to be able to cover all unequal pairs of
- // names with one from leftNames and one from rightNames
+ return json.Unmarshal(encrypted.Data, encrypted)
+}
- // Combinatorially, the number of ordered pairs with one element
- // from one set and one from another for which both elements of
- // the pair is distinct is
- // len(n) * len(k) - overlap
- encrypted.KeySet = make([]MultiWrappedKey, (len(leftNames)*len(rightNames) - overlap))
- encrypted.KeySetRSA = make(map[string]SingleWrappedKey)
-
- var singleWrappedKey SingleWrappedKey
- for name := range names {
- rec, ok := passvault.GetRecord(name)
+// wrapKey encrypts the clear key such that a minimum number of delegated keys
+// are required to decrypt. NOTE: Currently the max value for min is 2.
+func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []byte, access AccessStructure) (err error) {
+ generateRandomKey := func(name string) (singleWrappedKey SingleWrappedKey, err error) {
+ rec, ok := records.GetRecord(name)
if !ok {
err = errors.New("Missing user on disk")
return
}
- if rec.GetType() == passvault.RSARecord || rec.GetType() == passvault.ECCRecord {
- // only wrap key with RSA key if found
- if singleWrappedKey.aesKey, err = symcrypt.MakeRandom(16); err != nil {
- return nil, err
- }
+ if singleWrappedKey.aesKey, err = symcrypt.MakeRandom(16); err != nil {
+ return
+ }
- if singleWrappedKey.Key, err = rec.EncryptKey(singleWrappedKey.aesKey); err != nil {
- return nil, err
+ if singleWrappedKey.Key, err = rec.EncryptKey(singleWrappedKey.aesKey); err != nil {
+ return
+ }
+
+ return
+ }
+
+ encryptKey := func(outer, inner string, clearKey []byte) (keyBytes []byte, err error) {
+ var outerCrypt, innerCrypt cipher.Block
+ keyBytes = make([]byte, 16)
+
+ outerCrypt, err = aes.NewCipher(encrypted.KeySetRSA[outer].aesKey)
+ if err != nil {
+ return
+ }
+
+ innerCrypt, err = aes.NewCipher(encrypted.KeySetRSA[inner].aesKey)
+ if err != nil {
+ return
+ }
+
+ innerCrypt.Encrypt(keyBytes, clearKey)
+ outerCrypt.Encrypt(keyBytes, keyBytes)
+
+ return
+ }
+
+ if len(access.Names) > 0 {
+ // Generate a random AES key for each user and RSA/ECIES encrypt it
+ encrypted.KeySetRSA = make(map[string]SingleWrappedKey)
+
+ for _, name := range access.Names {
+ encrypted.KeySetRSA[name], err = generateRandomKey(name)
+ if err != nil {
+ return err
}
- encrypted.KeySetRSA[name] = singleWrappedKey
- } else {
- err = nil
+ }
+
+ // encrypt file key with every combination of two keys
+ encrypted.KeySet = make([]MultiWrappedKey, 0)
+
+ for i := 0; i < len(access.Names); i++ {
+ for j := i + 1; j < len(access.Names); j++ {
+ keyBytes, err := encryptKey(access.Names[i], access.Names[j], clearKey)
+ if err != nil {
+ return err
+ }
+
+ out := MultiWrappedKey{
+ Name: []string{access.Names[i], access.Names[j]},
+ Key: keyBytes,
+ }
+
+ encrypted.KeySet = append(encrypted.KeySet, out)
+ }
+ }
+
+ return nil
+ } else if len(access.LeftNames) > 0 && len(access.RightNames) > 0 {
+ // Generate a random AES key for each user and RSA/ECIES encrypt it
+ encrypted.KeySetRSA = make(map[string]SingleWrappedKey)
+
+ for _, name := range access.LeftNames {
+ encrypted.KeySetRSA[name], err = generateRandomKey(name)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, name := range access.RightNames {
+ encrypted.KeySetRSA[name], err = generateRandomKey(name)
+ if err != nil {
+ return err
+ }
+ }
+
+ // encrypt file key with every combination of one left key and one right key
+ encrypted.KeySet = make([]MultiWrappedKey, 0)
+
+ for _, leftName := range access.LeftNames {
+ for _, rightName := range access.RightNames {
+ keyBytes, err := encryptKey(leftName, rightName, clearKey)
+ if err != nil {
+ return err
+ }
+
+ out := MultiWrappedKey{
+ Name: []string{leftName, rightName},
+ Key: keyBytes,
+ }
+
+ encrypted.KeySet = append(encrypted.KeySet, out)
+ }
+ }
+
+ return nil
+ } else {
+ return errors.New("Invalid access structure.")
+ }
+}
+
+// unwrapKey decrypts first key in keys whose encryption keys are in keycache
+func (encrypted *EncryptedData) unwrapKey(cache *keycache.Cache, user string) (unwrappedKey []byte, names []string, err error) {
+ var (
+ keyFound error
+ fullMatch bool = false
+ nameSet = map[string]bool{}
+ )
+
+ for _, mwKey := range encrypted.KeySet {
+ // validate the size of the keys
+ if len(mwKey.Key) != 16 {
+ err = errors.New("Invalid Input")
+ }
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ tmpKeyValue := mwKey.Key
+
+ for _, mwName := range mwKey.Name {
+ pubEncrypted := encrypted.KeySetRSA[mwName]
+ // if this is null, it's an AES encrypted key
+ if tmpKeyValue, keyFound = cache.DecryptKey(tmpKeyValue, mwName, user, encrypted.Labels, pubEncrypted.Key); keyFound != nil {
+ break
+ }
+ nameSet[mwName] = true
+ }
+ if keyFound == nil {
+ fullMatch = true
+ // concatenate all the decrypted bytes
+ unwrappedKey = tmpKeyValue
+ break
}
}
- // encrypt file key with every combination of two keys
- var n int
- for _, nameOuter := range leftNames {
- for _, nameInner := range rightNames {
- if nameInner != nameOuter {
- encrypted.KeySet[n], err = encryptKey(nameInner, nameOuter, clearKey, encrypted.KeySetRSA)
- n += 1
- }
- if err != nil {
- return
- }
- }
+ if !fullMatch {
+ err = errors.New("Need more delegated keys")
+ names = nil
+ }
+
+ names = make([]string, 0, len(nameSet))
+ for name := range nameSet {
+ names = append(names, name)
+ }
+ return
+}
+
+// Encrypt encrypts data with the keys associated with names. This
+// requires a minimum of min keys to decrypt. NOTE: as currently
+// implemented, the maximum value for min is 2.
+func (c *Cryptor) Encrypt(in []byte, labels []string, access AccessStructure) (resp []byte, err error) {
+ var encrypted EncryptedData
+ encrypted.Version = DEFAULT_VERSION
+ if encrypted.VaultId, err = c.records.GetVaultID(); err != nil {
+ return
+ }
+
+ // Generate random IV and encryption key
+ encrypted.IV, err = symcrypt.MakeRandom(16)
+ if err != nil {
+ return
+ }
+
+ clearKey, err := symcrypt.MakeRandom(16)
+ if err != nil {
+ return
+ }
+
+ err = encrypted.wrapKey(c.records, clearKey, access)
+ if err != nil {
+ return
}
// encrypt file with clear key
@@ -363,55 +387,55 @@ func Encrypt(in []byte, labels, leftNames, rightNames []string, min int) (resp [
clearFile := padding.AddPadding(in)
encryptedFile := make([]byte, len(clearFile))
- aesCBC := cipher.NewCBCEncrypter(aesCrypt, ivBytes)
+ aesCBC := cipher.NewCBCEncrypter(aesCrypt, encrypted.IV)
aesCBC.CryptBlocks(encryptedFile, clearFile)
encrypted.Data = encryptedFile
encrypted.Labels = labels
- hmacKey, err := passvault.GetHmacKey()
+ hmacKey, err := c.records.GetHMACKey()
if err != nil {
return
}
- encrypted.Signature = computeHmac(hmacKey, encrypted)
+ encrypted.Signature = encrypted.computeHmac(hmacKey)
+ encrypted.lock(hmacKey)
return json.Marshal(encrypted)
}
// Decrypt decrypts a file using the keys in the key cache.
-func Decrypt(in []byte, user string) (resp []byte, names []string, err error) {
+func (c *Cryptor) Decrypt(in []byte, user string) (resp []byte, names []string, secure bool, err error) {
// unwrap encrypted file
var encrypted EncryptedData
if err = json.Unmarshal(in, &encrypted); err != nil {
return
}
- if encrypted.Version != DEFAULT_VERSION {
- return nil, nil, errors.New("Unknown version")
+ if encrypted.Version != DEFAULT_VERSION && encrypted.Version != -1 {
+ return nil, nil, secure, errors.New("Unknown version")
+ }
+
+ secure = encrypted.Version == -1
+
+ hmacKey, err := c.records.GetHMACKey()
+ if err != nil {
+ return
+ }
+
+ if err = encrypted.unlock(hmacKey); err != nil {
+ return
}
// make sure file was encrypted with the active vault
- vaultId, err := passvault.GetVaultId()
+ vaultId, err := c.records.GetVaultID()
if err != nil {
return
}
if encrypted.VaultId != vaultId {
- return nil, nil, errors.New("Wrong vault")
- }
-
- // validate the size of the keys
- for _, multiKey := range encrypted.KeySet {
- if len(multiKey.Key) != 16 {
- err = errors.New("Invalid Input")
- return
- }
+ return nil, nil, secure, errors.New("Wrong vault")
}
// compute HMAC
- hmacKey, err := passvault.GetHmacKey()
- if err != nil {
- return
- }
- expectedMAC := computeHmac(hmacKey, encrypted)
+ expectedMAC := encrypted.computeHmac(hmacKey)
if !hmac.Equal(encrypted.Signature, expectedMAC) {
err = errors.New("Signature mismatch")
return
@@ -419,7 +443,7 @@ func Decrypt(in []byte, user string) (resp []byte, names []string, err error) {
// decrypt file key with delegate keys
var unwrappedKey = make([]byte, 16)
- unwrappedKey, names, err = unwrapKey(encrypted.KeySet, encrypted.KeySetRSA, user, encrypted.Labels)
+ unwrappedKey, names, err = encrypted.unwrapKey(c.cache, user)
if err != nil {
return
}
diff --git a/cryptor/cryptor_test.go b/cryptor/cryptor_test.go
index d3d2d3a..011c036 100644
--- a/cryptor/cryptor_test.go
+++ b/cryptor/cryptor_test.go
@@ -22,7 +22,7 @@ func TestHash(t *testing.T) {
var hmacKey, _ = base64.StdEncoding.DecodeString("Qugc5ZQ0vC7KQSgmDHTVgQ==")
var signature = append([]byte{}, encrypted.Signature...)
- expectedSig := computeHmac(hmacKey, encrypted)
+ expectedSig := encrypted.computeHmac(hmacKey)
if diff := bytes.Compare(signature, expectedSig); diff != 0 {
t.Fatalf("Error comparing signature %v", base64.StdEncoding.EncodeToString(expectedSig))
@@ -30,7 +30,7 @@ func TestHash(t *testing.T) {
// change version and check hmac
encrypted.Version = 2
- unexpectedSig := computeHmac(hmacKey, encrypted)
+ unexpectedSig := encrypted.computeHmac(hmacKey)
if diff := bytes.Compare(signature, unexpectedSig); diff == 0 {
t.Fatalf("Error comparing signature")
@@ -39,7 +39,7 @@ func TestHash(t *testing.T) {
// change vaultid and check hmac
encrypted.VaultId = 529853896
- unexpectedSig = computeHmac(hmacKey, encrypted)
+ unexpectedSig = encrypted.computeHmac(hmacKey)
if diff := bytes.Compare(signature, unexpectedSig); diff == 0 {
t.Fatalf("Error comparing signature")
@@ -48,7 +48,7 @@ func TestHash(t *testing.T) {
// swap two records and check hmac
encrypted.KeySet[0], encrypted.KeySet[1] = encrypted.KeySet[1], encrypted.KeySet[0]
- unexpectedSig = computeHmac(hmacKey, encrypted)
+ unexpectedSig = encrypted.computeHmac(hmacKey)
if diff := bytes.Compare(signature, unexpectedSig); diff != 0 {
t.Fatalf("Error comparing signature %v, %v",
@@ -59,7 +59,7 @@ func TestHash(t *testing.T) {
// delete RSA key and check hmac
encrypted.Version = 1
delete(encrypted.KeySetRSA, "Carol")
- unexpectedSig = computeHmac(hmacKey, encrypted)
+ unexpectedSig = encrypted.computeHmac(hmacKey)
if diff := bytes.Compare(signature, unexpectedSig); diff == 0 {
t.Fatalf("Error comparing signature")
diff --git a/index.html b/index.html
index c3812be..20a7889 100644
--- a/index.html
+++ b/index.html
@@ -385,10 +385,12 @@
data.Users = data.Users.split(',');
for(var i=0, l=data.Users.length; i
'+ window.atob(d.Data)+'
Delegates: '+d.Delegates.sort().join(', ')+'
' }) ); + $form.find('.feedback').empty().append( makeAlert({ type: (d.Secure ? 'success' : 'warning'), message: 'Successfully decrypted data:
'+ window.atob(d.Data)+'
Delegates: '+d.Delegates.sort().join(', ')+'
' }) ); } }); }); diff --git a/keycache/keycache.go b/keycache/keycache.go index 95d0007..2d1886a 100644 --- a/keycache/keycache.go +++ b/keycache/keycache.go @@ -19,9 +19,6 @@ import ( "github.com/cloudflare/redoctober/passvault" ) -// UserKeys is the set of decrypted keys in memory, indexed by name. -var UserKeys map[string]ActiveUser = make(map[string]ActiveUser) - // Usage holds the permissions of a delegated permission type Usage struct { Uses int // Number of uses delegated @@ -36,29 +33,12 @@ type ActiveUser struct { Admin bool Type string - aesKey []byte rsaKey rsa.PrivateKey eccKey *ecdsa.PrivateKey } -// matchUser returns the matching active user if present -// and a boolean to indicate its presence. -func matchUser(name, user string, labels []string) (out ActiveUser, present bool) { - key, present := UserKeys[name] - if present { - if key.Usage.matches(user, labels) { - return key, true - } else { - present = false - } - } - - return -} - -// setUser takes an ActiveUser and adds it to the cache. -func setUser(in ActiveUser, name string) { - UserKeys[name] = in +type Cache struct { + UserKeys map[string]ActiveUser // Decrypted keys in memory, indexed by name. } // matchesLabel returns true if this usage applies the user and label @@ -68,7 +48,7 @@ func (usage Usage) matchesLabel(labels []string) bool { if len(labels) == 0 { return true } - // + for _, validLabel := range usage.Labels { for _, label := range labels { if label == validLabel { @@ -97,42 +77,66 @@ func (usage Usage) matches(user string, labels []string) bool { return false } +func NewCache() Cache { + return Cache{make(map[string]ActiveUser)} +} + +// setUser takes an ActiveUser and adds it to the cache. +func (cache *Cache) setUser(in ActiveUser, name string) { + cache.UserKeys[name] = in +} + +// matchUser returns the matching active user if present +// and a boolean to indicate its presence. +func (cache *Cache) matchUser(name, user string, labels []string) (out ActiveUser, present bool) { + key, present := cache.UserKeys[name] + if present { + if key.Usage.matches(user, labels) { + return key, true + } else { + present = false + } + } + + return +} + // useKey decrements the counter on an active key // for decryption or symmetric encryption -func useKey(name, user string, labels []string) { - if val, present := matchUser(name, user, labels); present { +func (cache *Cache) useKey(name, user string, labels []string) { + if val, present := cache.matchUser(name, user, labels); present { val.Usage.Uses -= 1 - setUser(val, name) + cache.setUser(val, name) } } // GetSummary returns the list of active user keys. -func GetSummary() map[string]ActiveUser { - return UserKeys +func (cache *Cache) GetSummary() map[string]ActiveUser { + return cache.UserKeys } // FlushCache removes all delegated keys. -func FlushCache() { - for name := range UserKeys { - delete(UserKeys, name) +func (cache *Cache) FlushCache() { + for name := range cache.UserKeys { + delete(cache.UserKeys, name) } } // Refresh purges all expired or used up keys. -func Refresh() { - for name, active := range UserKeys { +func (cache *Cache) Refresh() { + for name, active := range cache.UserKeys { if active.Usage.Expiry.Before(time.Now()) || active.Usage.Uses <= 0 { log.Println("Record expired", name, active.Usage.Users, active.Usage.Labels, active.Usage.Expiry) - delete(UserKeys, name) + delete(cache.UserKeys, name) } } } // AddKeyFromRecord decrypts a key for a given record and adds it to the cache. -func AddKeyFromRecord(record passvault.PasswordRecord, name, password string, users, labels []string, uses int, durationString string) (err error) { +func (cache *Cache) AddKeyFromRecord(record passvault.PasswordRecord, name, password string, users, labels []string, uses int, durationString string) (err error) { var current ActiveUser - Refresh() + cache.Refresh() // compute exipiration duration, err := time.ParseDuration(durationString) @@ -146,8 +150,6 @@ func AddKeyFromRecord(record passvault.PasswordRecord, name, password string, us // get decryption keys switch record.Type { - case passvault.AESRecord: - current.aesKey, err = record.GetKeyAES(password) case passvault.RSARecord: current.rsaKey, err = record.GetKeyRSA(password) case passvault.ECCRecord: @@ -165,58 +167,19 @@ func AddKeyFromRecord(record passvault.PasswordRecord, name, password string, us current.Admin = record.Admin // add current to map (overwriting previous for this name) - setUser(current, name) - - return -} - -// EncryptKey encrypts a 16 byte key using the cached key corresponding to name. -// For AES keys, use the cached key. -// For RSA and EC keys, the cache is not necessary; use the override -// key instead. -func EncryptKey(in []byte, name string, override []byte) (out []byte, err error) { - Refresh() - - aesKey := override - - // if the override key is not set, extract from the cache - if aesKey == nil { - encryptKey, ok := matchUser(name, name, []string{}) - if !ok { - return nil, errors.New("Key not delegated") - } - - switch encryptKey.Type { - case passvault.AESRecord: - aesKey = encryptKey.aesKey - - default: - return out, errors.New("Require override for key") - } - - useKey(name, name, []string{}) - } - - // encrypt - aesSession, err := aes.NewCipher(aesKey) - if err != nil { - return - } - out = make([]byte, 16) - aesSession.Encrypt(out, in) + cache.setUser(current, name) return } // DecryptKey decrypts a 16 byte key using the key corresponding to the name parameter -// for AES keys, the cached AES key is used directly to decrypt in -// for RSA and EC keys, the cached RSA/EC key is used to decrypt +// For RSA and EC keys, the cached RSA/EC key is used to decrypt // the pubEncryptedKey which is then used to decrypt the input // buffer. -func DecryptKey(in []byte, name, user string, labels []string, pubEncryptedKey []byte) (out []byte, err error) { - Refresh() +func (cache *Cache) DecryptKey(in []byte, name, user string, labels []string, pubEncryptedKey []byte) (out []byte, err error) { + cache.Refresh() - decryptKey, ok := matchUser(name, user, labels) + decryptKey, ok := cache.matchUser(name, user, labels) if !ok { return nil, errors.New("Key not delegated") } @@ -225,9 +188,6 @@ func DecryptKey(in []byte, name, user string, labels []string, pubEncryptedKey [ // pick the aesKey to use for decryption switch decryptKey.Type { - case passvault.AESRecord: - aesKey = decryptKey.aesKey - case passvault.RSARecord: // extract the aes key from the pubEncryptedKey aesKey, err = rsa.DecryptOAEP(sha1.New(), rand.Reader, &decryptKey.rsaKey, pubEncryptedKey, nil) @@ -253,7 +213,7 @@ func DecryptKey(in []byte, name, user string, labels []string, pubEncryptedKey [ out = make([]byte, 16) aesSession.Decrypt(out, in) - useKey(name, user, labels) + cache.useKey(name, user, labels) return } diff --git a/keycache/keycache_test.go b/keycache/keycache_test.go index 1a9ad7c..e70f640 100644 --- a/keycache/keycache_test.go +++ b/keycache/keycache_test.go @@ -4,230 +4,290 @@ package keycache import ( + "bytes" "github.com/cloudflare/redoctober/passvault" + "github.com/cloudflare/redoctober/symcrypt" "testing" "time" ) -var now = time.Now() -var nextYear = now.AddDate(1, 0, 0) -var emptyKey = make([]byte, 16) -var dummy = make([]byte, 16) - func TestUsesFlush(t *testing.T) { - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: nextYear, - Uses: 2, - }, - aesKey: emptyKey, + // Initialize passvault with one dummy user. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + // Initialize keycache and delegate the user's key to it. + cache := NewCache() + + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 2, "1h") + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - EncryptKey(dummy, "first", nil) - - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys %v", UserKeys) + // Generate a random symmetric key, encrypt a blank block with it, and encrypt + // the key itself with the user's public key. + dummy := make([]byte, 16) + key, err := symcrypt.MakeRandom(16) + if err != nil { + t.Fatalf("%v", err) } - DecryptKey(dummy, "first", "", []string{}, nil) + encKey, err := symcrypt.EncryptCBC(dummy, dummy, key) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 0 { - t.Fatalf("Error in number of live keys %v", UserKeys) + pubEncryptedKey, err := pr.EncryptKey(key) + if err != nil { + t.Fatalf("%v", err) + } + + key2, err := cache.DecryptKey(encKey, "user", "anybody", []string{}, pubEncryptedKey) + if err != nil { + t.Fatalf("%v", err) + } + + if bytes.Equal(key, key2) { + t.Fatalf("cache.DecryptKey didnt decrypt the right key!") + } + + // Second decryption allowed. + _, err = cache.DecryptKey(encKey, "user", "anybody else", []string{}, pubEncryptedKey) + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 0 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } func TestTimeFlush(t *testing.T) { - oneSec, _ := time.ParseDuration("1s") - one := now.Add(oneSec) - - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: one, - Uses: 10, - }, - aesKey: emptyKey, + // Initialize passvault and keycache. Delegate a key for 1s, wait a + // second and then make sure that it's gone. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + cache := NewCache() + + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 10, "1s") + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - EncryptKey(dummy, "first", nil) + time.Sleep(time.Second) - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys") + dummy := make([]byte, 16) + pubEncryptedKey, err := pr.EncryptKey(dummy) + if err != nil { + t.Fatalf("%v", err) } - time.Sleep(oneSec) - - _, err := DecryptKey(dummy, "first", "", []string{}, nil) - + _, err = cache.DecryptKey(dummy, "user", "anybody", []string{}, pubEncryptedKey) if err == nil { t.Fatalf("Error in pruning expired key") } } func TestGoodLabel(t *testing.T) { - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: nextYear, - Uses: 2, - Labels: []string{"red"}, - }, - aesKey: emptyKey, + // Initialize passvault and keycache. Delegate a key with the tag "red" and + // verify that decryption with the tag "red" is allowed. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + cache := NewCache() + + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "1h") + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - EncryptKey(dummy, "first", nil) - - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys") + dummy := make([]byte, 16) + pubEncryptedKey, err := pr.EncryptKey(dummy) + if err != nil { + t.Fatalf("%v", err) } - DecryptKey(dummy, "first", "", []string{"red"}, nil) + _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"red"}, pubEncryptedKey) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 0 { - t.Fatalf("Error in number of live keys %v", UserKeys) + cache.Refresh() + if len(cache.UserKeys) != 0 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } func TestBadLabel(t *testing.T) { - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: nextYear, - Uses: 2, - Labels: []string{"red"}, - }, - aesKey: emptyKey, + // Initialize passvault and keycache. Delegate a key with the tag "red" and + // verify that decryption with the tag "blue" is disallowed. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + cache := NewCache() + + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "1h") + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - EncryptKey(dummy, "first", nil) - - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys") + dummy := make([]byte, 16) + pubEncryptedKey, err := pr.EncryptKey(dummy) + if err != nil { + t.Fatalf("%v", err) } - _, err := DecryptKey(dummy, "first", "", []string{"blue"}, nil) - + _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"blue"}, pubEncryptedKey) if err == nil { - t.Fatalf("Decryption of labeled key with no permission") + t.Fatalf("Decryption of labeled key allowed without permission.") } - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys %v", UserKeys) + cache.Refresh() + if len(cache.UserKeys) != 1 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } func TestGoodUser(t *testing.T) { - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: nextYear, - Uses: 2, - Users: []string{"ci", "buildeng", "first"}, - Labels: []string{"red", "blue"}, - }, - aesKey: emptyKey, + // Initialize passvault and keycache. Delegate a key with tag and user + // restrictions and verify that permissible decryption is allowed. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + cache := NewCache() + + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", + []string{"ci", "buildeng", "user"}, + []string{"red", "blue"}, + 1, "1h", + ) + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - EncryptKey(dummy, "first", nil) - - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys") + dummy := make([]byte, 16) + pubEncryptedKey, err := pr.EncryptKey(dummy) + if err != nil { + t.Fatalf("%v", err) } - DecryptKey(dummy, "first", "ci", []string{"red"}, nil) + _, err = cache.DecryptKey(dummy, "user", "ci", []string{"red"}, pubEncryptedKey) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 0 { - t.Fatalf("Error in number of live keys %v", UserKeys) + cache.Refresh() + if len(cache.UserKeys) != 0 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } func TestBadUser(t *testing.T) { - singleUse := ActiveUser{ - Admin: true, - Type: passvault.AESRecord, - Usage: Usage{ - Expiry: nextYear, - Uses: 2, - Users: []string{"ci", "buildeng", "first"}, - Labels: []string{"red", "blue"}, - }, - aesKey: emptyKey, + // Initialize passvault and keycache. Delegate a key with tag and user + // restrictions and verify that illegal decryption is disallowed. + records, err := passvault.InitFrom("memory") + if err != nil { + t.Fatalf("%v", err) } - UserKeys["first"] = singleUse + pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType) + if err != nil { + t.Fatalf("%v", err) + } - Refresh() - if len(UserKeys) != 1 { + cache := NewCache() + + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", + []string{"ci", "buildeng", "user"}, + []string{"red", "blue"}, + 1, "1h", + ) + if err != nil { + t.Fatalf("%v", err) + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys") } - // Note that the active user needs to be in the set of delegated - // users in the AES case only - EncryptKey(dummy, "first", nil) - - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys") + dummy := make([]byte, 16) + pubEncryptedKey, err := pr.EncryptKey(dummy) + if err != nil { + t.Fatalf("%v", err) } - _, err := DecryptKey(dummy, "first", "", []string{"blue"}, nil) - + _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"blue"}, pubEncryptedKey) if err == nil { - t.Fatalf("Decryption of labeled key by unauthorized user") + t.Fatalf("Decryption of labeled key allowed without permission.") } - Refresh() - if len(UserKeys) != 1 { - t.Fatalf("Error in number of live keys %v", UserKeys) + cache.Refresh() + if len(cache.UserKeys) != 1 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } diff --git a/passvault/passvault.go b/passvault/passvault.go index 21ff838..d81df4d 100644 --- a/passvault/passvault.go +++ b/passvault/passvault.go @@ -30,7 +30,6 @@ import ( // Constants for record type const ( - AESRecord = "AES" RSARecord = "RSA" ECCRecord = "ECC" ) @@ -47,9 +46,6 @@ const ( DEFAULT_VERSION = 1 ) -// Path of current vault -var localPath string - type ECPublicKey struct { Curve *elliptic.CurveParams X, Y *big.Int @@ -74,7 +70,6 @@ type PasswordRecord struct { PasswordSalt []byte HashedPassword []byte KeySalt []byte - AESKey []byte RSAKey struct { RSAExp []byte RSAExpIV []byte @@ -94,16 +89,14 @@ type PasswordRecord struct { // diskRecords is the structure used to read and write a JSON file // containing the contents of a password vault -type diskRecords struct { +type Records struct { Version int VaultId int HmacKey []byte Passwords map[string]PasswordRecord -} -// records is the set of encrypted records read from disk and -// unmarshalled -var records diskRecords + localPath string // Path of current vault +} // Summary is a minmial account summary. type Summary struct { @@ -173,8 +166,8 @@ 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 = DefaultRecordType +func createPasswordRec(password string, admin bool, userType string) (newRec PasswordRecord, err error) { + newRec.Type = userType if newRec.PasswordSalt, err = symcrypt.MakeRandom(16); err != nil { return @@ -194,7 +187,7 @@ func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err } // generate a key pair - switch DefaultRecordType { + switch userType { case RSARecord: var rsaPriv *rsa.PrivateKey rsaPriv, err = rsa.GenerateKey(rand.Reader, 2048) @@ -219,16 +212,8 @@ func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err newRec.ECKey.ECPublic.Curve = ecPriv.PublicKey.Curve.Params() newRec.ECKey.ECPublic.X = ecPriv.PublicKey.X newRec.ECKey.ECPublic.Y = ecPriv.PublicKey.Y - } - - // encrypt AES key with password key - aesKey, err := symcrypt.MakeRandom(16) - if err != nil { - return - } - - if newRec.AESKey, err = encryptECB(aesKey, passKey); err != nil { - return + default: + err = errors.New("Unknown record type") } newRec.Admin = admin @@ -269,14 +254,18 @@ func encryptECB(data, key []byte) (encryptedData []byte, err error) { } // InitFromDisk reads the record from disk and initialize global context. -func InitFromDisk(path string) error { - jsonDiskRecord, err := ioutil.ReadFile(path) +func InitFrom(path string) (records Records, err error) { + var jsonDiskRecord []byte - // It's OK for the file to be missing, we'll create it later if - // anything is added. + if path != "memory" { + jsonDiskRecord, err = ioutil.ReadFile(path) - if err != nil && !os.IsNotExist(err) { - return err + // It's OK for the file to be missing, we'll create it later if + // anything is added. + + if err != nil && !os.IsNotExist(err) { + return + } } // Initialized so that we can determine later if anything was read @@ -286,49 +275,47 @@ func InitFromDisk(path string) error { if len(jsonDiskRecord) != 0 { if err = json.Unmarshal(jsonDiskRecord, &records); err != nil { - return err + return } } - formatErr := errors.New("Format error") + err = errors.New("Format error") for _, rec := range records.Passwords { if len(rec.PasswordSalt) != 16 { - return formatErr + return } if len(rec.HashedPassword) != 16 { - return formatErr + return } if len(rec.KeySalt) != 16 { - return formatErr - } - if rec.Type == AESRecord { - if len(rec.AESKey) != 16 { - return formatErr - } + return } if rec.Type == RSARecord { if len(rec.RSAKey.RSAExp) == 0 || len(rec.RSAKey.RSAExp)%16 != 0 { - return formatErr + return } if len(rec.RSAKey.RSAPrimeP) == 0 || len(rec.RSAKey.RSAPrimeP)%16 != 0 { - return formatErr + return } if len(rec.RSAKey.RSAPrimeQ) == 0 || len(rec.RSAKey.RSAPrimeQ)%16 != 0 { - return formatErr + return } if len(rec.RSAKey.RSAExpIV) != 16 { - return formatErr + return } if len(rec.RSAKey.RSAPrimePIV) != 16 { - return formatErr + return } if len(rec.RSAKey.RSAPrimeQIV) != 16 { - return formatErr + return } } if rec.Type == ECCRecord { - if len(rec.ECKey.ECPriv) == 0 { - return formatErr + if len(rec.ECKey.ECPriv) == 0 || len(rec.ECKey.ECPriv)%16 != 0 { + return + } + if len(rec.ECKey.ECPrivIV) != 16 { + return } } } @@ -341,69 +328,82 @@ func InitFromDisk(path string) error { records.VaultId = int(mrand.Int31()) records.HmacKey, err = symcrypt.MakeRandom(16) if err != nil { - return err + return } records.Passwords = make(map[string]PasswordRecord) } - localPath = path + records.localPath = path - return nil + err = nil + return } // WriteRecordsToDisk saves the current state of the records to disk. -func WriteRecordsToDisk() error { - if !IsInitialized() { - return errors.New("Path not initialized") +func (records *Records) WriteRecordsToDisk() error { + if records.localPath == "memory" { + return nil } jsonDiskRecord, err := json.Marshal(records) if err != nil { return err } - return ioutil.WriteFile(localPath, jsonDiskRecord, 0644) + return ioutil.WriteFile(records.localPath, jsonDiskRecord, 0644) } // AddNewRecord adds a new record for a given username and password. -func AddNewRecord(name, password string, admin bool) (PasswordRecord, error) { - pr, err := createPasswordRec(password, admin) +func (records *Records) AddNewRecord(name, password string, admin bool, userType string) (PasswordRecord, error) { + pr, err := createPasswordRec(password, admin, userType) if err != nil { return pr, err } - SetRecord(pr, name) - return pr, WriteRecordsToDisk() + records.SetRecord(pr, name) + return pr, records.WriteRecordsToDisk() } // ChangePassword changes the password for a given user. -func ChangePassword(name, password, newPassword string) (err error) { - pr, ok := GetRecord(name) +func (records *Records) ChangePassword(name, password, newPassword string) (err error) { + pr, ok := records.GetRecord(name) if !ok { err = errors.New("Record not present") return } - if err = pr.ValidatePassword(password); err != nil { + + var keySalt []byte + if keySalt, err = symcrypt.MakeRandom(16); err != nil { + return + } + newPassKey, err := derivePasswordKey(newPassword, keySalt) + if err != nil { return } - // 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 { - return - } - } else if pr.Type == RSARecord { + // decrypt with old password and re-encrypt original key with new password + if pr.Type == RSARecord { + var rsaKey rsa.PrivateKey rsaKey, err = pr.GetKeyRSA(password) if err != nil { return } + + // encrypt RSA key with password key + err = encryptRSARecord(&pr, &rsaKey, newPassKey) + if err != nil { + return + } } else if pr.Type == ECCRecord { + var ecKey *ecdsa.PrivateKey ecKey, err = pr.GetKeyECC(password) if err != nil { return } + + // encrypt ECDSA key with password key + err = encryptECCRecord(&pr, ecKey, newPassKey) + if err != nil { + return + } } else { err = errors.New("Unkown record type") return @@ -417,115 +417,70 @@ func ChangePassword(name, password, newPassword string) (err error) { return } - if pr.KeySalt, err = symcrypt.MakeRandom(16); err != nil { - return - } - newPassKey, err := derivePasswordKey(newPassword, pr.KeySalt) - if err != nil { - return - } + pr.KeySalt = keySalt - // encrypt original key with new password - if pr.Type == AESRecord { - pr.AESKey, err = encryptECB(key, newPassKey) - if err != nil { - return - } - } else if pr.Type == RSARecord { - // encrypt RSA key with password key - err = encryptRSARecord(&pr, &rsaKey, newPassKey) - 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 - } + records.SetRecord(pr, name) - SetRecord(pr, name) - - return WriteRecordsToDisk() + return records.WriteRecordsToDisk() } // DeleteRecord deletes a given record. -func DeleteRecord(name string) error { - if _, ok := GetRecord(name); ok { +func (records *Records) DeleteRecord(name string) error { + if _, ok := records.GetRecord(name); ok { delete(records.Passwords, name) - return WriteRecordsToDisk() + return records.WriteRecordsToDisk() } - return errors.New("Record missing") } // RevokeRecord removes admin status from a record. -func RevokeRecord(name string) error { - if rec, ok := GetRecord(name); ok { +func (records *Records) RevokeRecord(name string) error { + if rec, ok := records.GetRecord(name); ok { rec.Admin = false - SetRecord(rec, name) - return WriteRecordsToDisk() + records.SetRecord(rec, name) + return records.WriteRecordsToDisk() } - return errors.New("Record missing") } // MakeAdmin adds admin status to a given record. -func MakeAdmin(name string) error { - if rec, ok := GetRecord(name); ok { +func (records *Records) MakeAdmin(name string) error { + if rec, ok := records.GetRecord(name); ok { rec.Admin = true - SetRecord(rec, name) - return WriteRecordsToDisk() + records.SetRecord(rec, name) + return records.WriteRecordsToDisk() } - return errors.New("Record missing") } // SetRecord puts a record into the global status. -func SetRecord(pr PasswordRecord, name string) { +func (records *Records) SetRecord(pr PasswordRecord, name string) { records.Passwords[name] = pr } // GetRecord returns a record given a name. -func GetRecord(name string) (PasswordRecord, bool) { +func (records *Records) GetRecord(name string) (PasswordRecord, bool) { dpr, found := records.Passwords[name] return dpr, found } // GetVaultId returns the id of the current vault. -func GetVaultId() (id int, err error) { - if !IsInitialized() { - return 0, errors.New("Path not initialized") - } - +func (records *Records) GetVaultID() (id int, err error) { return records.VaultId, nil } // GetHmacKey returns the hmac key of the current vault. -func GetHmacKey() (key []byte, err error) { - if !IsInitialized() { - return nil, errors.New("Path not initialized") - } - +func (records *Records) GetHMACKey() (key []byte, err error) { return records.HmacKey, nil } -// IsInitialized returns true if the disk vault has been loaded. -func IsInitialized() bool { - return localPath != "" -} - // NumRecords returns the number of records in the vault. -func NumRecords() int { +func (records *Records) NumRecords() int { return len(records.Passwords) } // GetSummary returns a summary of the records on disk. -func GetSummary() (summary map[string]Summary) { +func (records *Records) GetSummary() (summary map[string]Summary) { summary = make(map[string]Summary) for name, pass := range records.Passwords { summary[name] = Summary{pass.Admin, pass.Type} @@ -534,17 +489,17 @@ func GetSummary() (summary map[string]Summary) { } // IsAdmin returns the admin status of the PasswordRecord. -func (pr PasswordRecord) IsAdmin() bool { +func (pr *PasswordRecord) IsAdmin() bool { return pr.Admin } // GetType returns the type status of the PasswordRecord. -func (pr PasswordRecord) GetType() string { +func (pr *PasswordRecord) GetType() string { return pr.Type } // 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) { +func (pr *PasswordRecord) EncryptKey(in []byte) (out []byte, err error) { if pr.Type == RSARecord { return rsa.EncryptOAEP(sha1.New(), rand.Reader, &pr.RSAKey.RSAPublic, in, nil) } else if pr.Type == ECCRecord { @@ -554,27 +509,8 @@ func (pr PasswordRecord) EncryptKey(in []byte) (out []byte, err error) { } } -// GetKeyAES returns the 16-byte key of the record. -func (pr PasswordRecord) GetKeyAES(password string) (key []byte, err error) { - if pr.Type != AESRecord { - return nil, errors.New("Invalid function for record type") - } - - err = pr.ValidatePassword(password) - if err != nil { - return - } - - passKey, err := derivePasswordKey(password, pr.KeySalt) - if err != nil { - return - } - - return decryptECB(pr.AESKey, passKey) -} - // GetKeyRSAPub returns the RSA public key of the record. -func (pr PasswordRecord) GetKeyRSAPub() (out *rsa.PublicKey, err error) { +func (pr *PasswordRecord) GetKeyRSAPub() (out *rsa.PublicKey, err error) { if pr.Type != RSARecord { return out, errors.New("Invalid function for record type") } @@ -582,7 +518,7 @@ func (pr PasswordRecord) GetKeyRSAPub() (out *rsa.PublicKey, err error) { } // GetKeyECCPub returns the ECDSA public key out of the record. -func (pr PasswordRecord) GetKeyECCPub() (out *ecdsa.PublicKey, err error) { +func (pr *PasswordRecord) GetKeyECCPub() (out *ecdsa.PublicKey, err error) { if pr.Type != ECCRecord { return out, errors.New("Invalid function for record type") } @@ -590,7 +526,7 @@ func (pr PasswordRecord) GetKeyECCPub() (out *ecdsa.PublicKey, err error) { } // GetKeyECC returns the ECDSA private key of the record given the correct password. -func (pr PasswordRecord) GetKeyECC(password string) (key *ecdsa.PrivateKey, err error) { +func (pr *PasswordRecord) GetKeyECC(password string) (key *ecdsa.PrivateKey, err error) { if pr.Type != ECCRecord { return key, errors.New("Invalid function for record type") } @@ -617,7 +553,7 @@ func (pr PasswordRecord) GetKeyECC(password string) (key *ecdsa.PrivateKey, err } // GetKeyRSA returns the RSA private key of the record given the correct password. -func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err error) { +func (pr *PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err error) { if pr.Type != RSARecord { return key, errors.New("Invalid function for record type") } @@ -674,14 +610,14 @@ func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err err } // ValidatePassword returns an error if the password is incorrect. -func (pr PasswordRecord) ValidatePassword(password string) error { - if h, err := hashPassword(password, pr.PasswordSalt); err != nil { +func (pr *PasswordRecord) ValidatePassword(password string) error { + h, err := hashPassword(password, pr.PasswordSalt) + if err != nil { return err - } else { - if bytes.Compare(h, pr.HashedPassword) != 0 { - return errors.New("Wrong Password") - } } + if bytes.Compare(h, pr.HashedPassword) != 0 { + return errors.New("Wrong Password") + } return nil } diff --git a/passvault/passvault_test.go b/passvault/passvault_test.go index 51f54e0..7516520 100644 --- a/passvault/passvault_test.go +++ b/passvault/passvault_test.go @@ -5,33 +5,42 @@ package passvault import ( - "testing" "os" + "testing" ) func TestStaticVault(t *testing.T) { - err := InitFromDisk("/tmp/redoctober.json") + // Creates a temporary on-disk database to test if passvault can read and + // write from/to disk. It's deleted at the bottom of the function--this + // should be the only test that requires touching disk. + records, err := InitFrom("/tmp/redoctober.json") if err != nil { t.Fatalf("Error reading record") } - _, err = AddNewRecord("test", "bad pass", true) + _, err = records.AddNewRecord("test", "bad pass", true, DefaultRecordType) if err != nil { t.Fatalf("Error creating record") } - err = InitFromDisk("/tmp/redoctober.json") + + // Reads data written last time. + _, err = InitFrom("/tmp/redoctober.json") if err != nil { t.Fatalf("Error reading record") } + os.Remove("/tmp/redoctober.json") } func TestRSAEncryptDecrypt(t *testing.T) { - oldDefaultRecordType := DefaultRecordType - DefaultRecordType = RSARecord - myRec, err := createPasswordRec("mypasswordisweak", true) + records, err := InitFrom("memory") if err != nil { - t.Fatalf("Error creating record") + t.Fatalf("%v", err) + } + + myRec, err := records.AddNewRecord("user", "weakpassword", true, RSARecord) + if err != nil { + t.Fatalf("%v", err) } _, err = myRec.GetKeyRSAPub() @@ -44,7 +53,7 @@ func TestRSAEncryptDecrypt(t *testing.T) { t.Fatalf("Incorrect password did not fail") } - rsaPriv, err = myRec.GetKeyRSA("mypasswordisweak") + rsaPriv, err = myRec.GetKeyRSA("weakpassword") if err != nil { t.Fatalf("Error decrypting RSA key") } @@ -53,20 +62,22 @@ 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) + records, err := InitFrom("memory") if err != nil { - t.Fatalf("Error creating record") + t.Fatalf("%v", err) + } + + myRec, err := records.AddNewRecord("user", "weakpassword", true, ECCRecord) + if err != nil { + t.Fatalf("%v", err) } _, err = myRec.GetKeyECCPub() if err != nil { - t.Fatalf("Error extracting EC pub") + t.Fatalf("%v", err) } _, err = myRec.GetKeyECC("mypasswordiswrong") @@ -74,9 +85,8 @@ func TestECCEncryptDecrypt(t *testing.T) { t.Fatalf("Incorrect password did not fail") } - _, err = myRec.GetKeyECC("mypasswordisweak") + _, err = myRec.GetKeyECC("weakpassword") if err != nil { - t.Fatalf("Error decrypting EC key") + t.Fatalf("%v", err) } - DefaultRecordType = oldDefaultRecordType } diff --git a/redoctober.go b/redoctober.go index 90ec880..e7e909b 100644 --- a/redoctober.go +++ b/redoctober.go @@ -89,7 +89,7 @@ func NewServer(process chan<- userRequest, staticPath, addr, certPath, keyPath, Rand: rand.Reader, PreferServerCipherSuites: true, SessionTicketsDisabled: true, - MinVersion: tls.VersionTLS10, + MinVersion: tls.VersionTLS10, } // If a caPath has been specified then a local CA is being used @@ -632,10 +632,12 @@ var indexHtml = []byte(` data.Users = data.Users.split(','); for(var i=0, l=data.Users.length; iSuccessfully decrypted data:
'+ window.atob(d.Data)+'
Delegates: '+d.Delegates.sort().join(', ')+'
' }) ); + $form.find('.feedback').empty().append( makeAlert({ type: (d.Secure ? 'success' : 'warning'), message: 'Successfully decrypted data:
'+ window.atob(d.Data)+'
Delegates: '+d.Delegates.sort().join(', ')+'
' }) ); } }); });