mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-10 07:58:03 +00:00
Merge pull request #50 from Bren2010/patch05
Patches for Bugs & HMAC Malleability
This commit is contained in:
64
core/core.go
64
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"))
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -385,10 +385,12 @@
|
||||
data.Users = data.Users.split(',');
|
||||
for(var i=0, l=data.Users.length; i<l; i++){
|
||||
data.Users[i] = data.Users[i].trim();
|
||||
if (data.Users[i] == "") { data.Users.splice(i, 1); }
|
||||
}
|
||||
data.Labels = data.Labels.split(',');
|
||||
for(var i=0, l=data.Labels.length; i<l; i++){
|
||||
data.Labels[i] = data.Labels[i].trim();
|
||||
if (data.Labels[i] == "") { data.Labels.splice(i, 1); }
|
||||
}
|
||||
|
||||
submit( $form, {
|
||||
@@ -454,10 +456,12 @@
|
||||
data.Owners = data.Owners.split(',');
|
||||
for(var i=0, l=data.Owners.length; i<l; i++){
|
||||
data.Owners[i] = data.Owners[i].trim();
|
||||
if (data.Owners[i] == "") { data.Owners.splice(i, 1); }
|
||||
}
|
||||
data.Labels = data.Labels.split(',');
|
||||
for(var i=0, l=data.Labels.length; i<l; i++){
|
||||
data.Labels[i] = data.Labels[i].trim();
|
||||
if (data.Labels[i] == "") { data.Labels.splice(i, 1); }
|
||||
}
|
||||
|
||||
// Convert data to base64.
|
||||
@@ -481,7 +485,7 @@
|
||||
data : data,
|
||||
success : function(d){
|
||||
d = JSON.parse(window.atob(d.Response));
|
||||
$form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '<p>Successfully decrypted data:</p><pre>'+ window.atob(d.Data)+'</pre><p>Delegates: '+d.Delegates.sort().join(', ')+'</p>' }) );
|
||||
$form.find('.feedback').empty().append( makeAlert({ type: (d.Secure ? 'success' : 'warning'), message: '<p>Successfully decrypted data:</p><pre>'+ window.atob(d.Data)+'</pre><p>Delegates: '+d.Delegates.sort().join(', ')+'</p>' }) );
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(`<!DOCTYPE html>
|
||||
data.Users = data.Users.split(',');
|
||||
for(var i=0, l=data.Users.length; i<l; i++){
|
||||
data.Users[i] = data.Users[i].trim();
|
||||
if (data.Users[i] == "") { data.Users.splice(i, 1); }
|
||||
}
|
||||
data.Labels = data.Labels.split(',');
|
||||
for(var i=0, l=data.Labels.length; i<l; i++){
|
||||
data.Labels[i] = data.Labels[i].trim();
|
||||
if (data.Labels[i] == "") { data.Labels.splice(i, 1); }
|
||||
}
|
||||
|
||||
submit( $form, {
|
||||
@@ -701,10 +703,12 @@ var indexHtml = []byte(`<!DOCTYPE html>
|
||||
data.Owners = data.Owners.split(',');
|
||||
for(var i=0, l=data.Owners.length; i<l; i++){
|
||||
data.Owners[i] = data.Owners[i].trim();
|
||||
if (data.Owners[i] == "") { data.Owners.splice(i, 1); }
|
||||
}
|
||||
data.Labels = data.Labels.split(',');
|
||||
for(var i=0, l=data.Labels.length; i<l; i++){
|
||||
data.Labels[i] = data.Labels[i].trim();
|
||||
if (data.Labels[i] == "") { data.Labels.splice(i, 1); }
|
||||
}
|
||||
|
||||
// Convert data to base64.
|
||||
@@ -728,7 +732,7 @@ var indexHtml = []byte(`<!DOCTYPE html>
|
||||
data : data,
|
||||
success : function(d){
|
||||
d = JSON.parse(window.atob(d.Response));
|
||||
$form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '<p>Successfully decrypted data:</p><pre>'+ window.atob(d.Data)+'</pre><p>Delegates: '+d.Delegates.sort().join(', ')+'</p>' }) );
|
||||
$form.find('.feedback').empty().append( makeAlert({ type: (d.Secure ? 'success' : 'warning'), message: '<p>Successfully decrypted data:</p><pre>'+ window.atob(d.Data)+'</pre><p>Delegates: '+d.Delegates.sort().join(', ')+'</p>' }) );
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user