mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-06 21:47:37 +00:00
Integration with Bren2010/MSP
This commit is contained in:
committed by
Brendan McMillion
parent
c0932d9cb5
commit
becabb40e9
@@ -72,6 +72,7 @@ type EncryptRequest struct {
|
||||
Owners []string
|
||||
LeftOwners []string
|
||||
RightOwners []string
|
||||
Predicate string
|
||||
|
||||
Data []byte
|
||||
|
||||
@@ -454,6 +455,7 @@ func Encrypt(jsonIn []byte) ([]byte, error) {
|
||||
Names: s.Owners,
|
||||
LeftNames: s.LeftOwners,
|
||||
RightNames: s.RightOwners,
|
||||
Predicate: s.Predicate,
|
||||
}
|
||||
|
||||
resp, err := crypt.Encrypt(s.Data, s.Labels, access)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/Bren2010/msp"
|
||||
"github.com/cloudflare/redoctober/keycache"
|
||||
"github.com/cloudflare/redoctober/padding"
|
||||
"github.com/cloudflare/redoctober/passvault"
|
||||
@@ -44,6 +45,39 @@ type AccessStructure struct {
|
||||
|
||||
LeftNames []string
|
||||
RightNames []string
|
||||
|
||||
Predicate string
|
||||
}
|
||||
|
||||
// Implements msp.UserDatabase
|
||||
type UserDatabase struct {
|
||||
records *passvault.Records
|
||||
cache *keycache.Cache
|
||||
|
||||
user string
|
||||
labels []string
|
||||
keySet map[string]SingleWrappedKey
|
||||
shareSet map[string][][]byte
|
||||
}
|
||||
|
||||
func (u UserDatabase) ValidUser(name string) bool {
|
||||
_, ok := u.records.GetRecord(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (u UserDatabase) CanGetShare(name string) bool {
|
||||
_, _, ok := u.cache.MatchUser(name, u.user, u.labels)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (u UserDatabase) GetShare(name string) ([][]byte, error) {
|
||||
return u.cache.DecryptShares(
|
||||
u.shareSet[name],
|
||||
name,
|
||||
u.user,
|
||||
u.labels,
|
||||
u.keySet[name].Key,
|
||||
)
|
||||
}
|
||||
|
||||
// MultiWrappedKey is a structure containing a 16-byte key encrypted
|
||||
@@ -67,8 +101,10 @@ type EncryptedData struct {
|
||||
Version int
|
||||
VaultId int `json:",omitempty"`
|
||||
Labels []string `json:",omitempty"`
|
||||
Predicate string `json:",omitempty"`
|
||||
KeySet []MultiWrappedKey `json:",omitempty"`
|
||||
KeySetRSA map[string]SingleWrappedKey `json:",omitempty"`
|
||||
ShareSet map[string][][]byte `json:",omitempty"`
|
||||
IV []byte `json:",omitempty"`
|
||||
Data []byte
|
||||
Signature []byte
|
||||
@@ -259,8 +295,6 @@ func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []b
|
||||
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)
|
||||
@@ -301,11 +335,41 @@ func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []b
|
||||
encrypted.KeySet = append(encrypted.KeySet, out)
|
||||
}
|
||||
}
|
||||
} else if len(access.Predicate) > 0 {
|
||||
encrypted.KeySetRSA = make(map[string]SingleWrappedKey)
|
||||
|
||||
return nil
|
||||
sss, err := msp.StringToMSP(access.Predicate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := msp.UserDatabase(UserDatabase{records: records})
|
||||
shareSet, err := sss.DistributeShares(clearKey, msp.Modulus(127), &db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name, _ := range shareSet {
|
||||
encrypted.KeySetRSA[name], err = generateRandomKey(name)
|
||||
crypt, err := aes.NewCipher(encrypted.KeySetRSA[name].aesKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, _ := range shareSet[name] {
|
||||
tmp := make([]byte, 16)
|
||||
crypt.Encrypt(tmp, shareSet[name][i])
|
||||
shareSet[name][i] = tmp
|
||||
}
|
||||
}
|
||||
|
||||
encrypted.ShareSet = shareSet
|
||||
encrypted.Predicate = access.Predicate
|
||||
} else {
|
||||
return errors.New("Invalid access structure.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// unwrapKey decrypts first key in keys whose encryption keys are in keycache
|
||||
@@ -316,50 +380,70 @@ func (encrypted *EncryptedData) unwrapKey(cache *keycache.Cache, user string) (u
|
||||
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 len(encrypted.Predicate) == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
// loop through users to see if they are all delegated
|
||||
fullMatch = true
|
||||
for _, mwName := range mwKey.Name {
|
||||
if valid := cache.Valid(mwName, user, encrypted.Labels); !valid {
|
||||
fullMatch = false
|
||||
break
|
||||
}
|
||||
nameSet[mwName] = true
|
||||
}
|
||||
|
||||
// if the keys are delegated, decrypt the mwKey with them
|
||||
if fullMatch == true {
|
||||
tmpKeyValue := mwKey.Key
|
||||
for _, mwName := range mwKey.Name {
|
||||
pubEncrypted := encrypted.KeySetRSA[mwName]
|
||||
if tmpKeyValue, keyFound = cache.DecryptKey(tmpKeyValue, mwName, user, encrypted.Labels, pubEncrypted.Key); keyFound != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
} else {
|
||||
var sss msp.MSP
|
||||
sss, err = msp.StringToMSP(encrypted.Predicate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// loop through users to see if they are all delegated
|
||||
fullMatch = true
|
||||
for _, mwName := range mwKey.Name {
|
||||
if valid := cache.Valid(mwName, user, encrypted.Labels); !valid {
|
||||
fullMatch = false
|
||||
break
|
||||
}
|
||||
nameSet[mwName] = true
|
||||
}
|
||||
db := msp.UserDatabase(UserDatabase{
|
||||
cache: cache,
|
||||
user: user,
|
||||
labels: encrypted.Labels,
|
||||
keySet: encrypted.KeySetRSA,
|
||||
shareSet: encrypted.ShareSet,
|
||||
})
|
||||
unwrappedKey, err = sss.RecoverSecret(msp.Modulus(127), &db)
|
||||
names = []string{"Shares"}
|
||||
|
||||
// if the keys are delegated, decrypt the mwKey with them
|
||||
if fullMatch == true {
|
||||
tmpKeyValue := mwKey.Key
|
||||
for _, mwName := range mwKey.Name {
|
||||
pubEncrypted := encrypted.KeySetRSA[mwName]
|
||||
if tmpKeyValue, keyFound = cache.DecryptKey(tmpKeyValue, mwName, user, encrypted.Labels, pubEncrypted.Key); keyFound != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
unwrappedKey = tmpKeyValue
|
||||
break
|
||||
}
|
||||
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
|
||||
|
||||
@@ -112,9 +112,9 @@ func (cache *Cache) Valid(name, user string, labels []string) (present bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// matchUser returns the matching active user if present
|
||||
// MatchUser returns the matching active user if present
|
||||
// and a boolean to indicate its presence.
|
||||
func (cache *Cache) matchUser(name, user string, labels []string) (ActiveUser, string, bool) {
|
||||
func (cache *Cache) MatchUser(name, user string, labels []string) (ActiveUser, string, bool) {
|
||||
var key ActiveUser
|
||||
for d, key := range cache.UserKeys {
|
||||
if d.Name != name {
|
||||
@@ -131,7 +131,7 @@ func (cache *Cache) matchUser(name, user string, labels []string) (ActiveUser, s
|
||||
// useKey decrements the counter on an active key
|
||||
// for decryption or symmetric encryption
|
||||
func (cache *Cache) useKey(name, user, slot string, labels []string) {
|
||||
if val, slot, present := cache.matchUser(name, user, labels); present {
|
||||
if val, slot, present := cache.MatchUser(name, user, labels); present {
|
||||
val.Usage.Uses -= 1
|
||||
cache.setUser(val, name, slot)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func (cache *Cache) AddKeyFromRecord(record passvault.PasswordRecord, name, pass
|
||||
func (cache *Cache) DecryptKey(in []byte, name, user string, labels []string, pubEncryptedKey []byte) (out []byte, err error) {
|
||||
cache.Refresh()
|
||||
|
||||
decryptKey, slot, ok := cache.matchUser(name, user, labels)
|
||||
decryptKey, slot, ok := cache.MatchUser(name, user, labels)
|
||||
if !ok {
|
||||
return nil, errors.New("Key not delegated")
|
||||
}
|
||||
@@ -252,3 +252,52 @@ func (cache *Cache) DecryptKey(in []byte, name, user string, labels []string, pu
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptShares decrypts an array of 16 byte shares using the key corresponding
|
||||
// to the name parameter.
|
||||
func (cache *Cache) DecryptShares(in [][]byte, name, user string, labels []string, pubEncryptedKey []byte) (out [][]byte, err error) {
|
||||
cache.Refresh()
|
||||
|
||||
decryptKey, slot, ok := cache.MatchUser(name, user, labels)
|
||||
if !ok {
|
||||
return nil, errors.New("Key not delegated")
|
||||
}
|
||||
|
||||
var aesKey []byte
|
||||
|
||||
// pick the aesKey to use for decryption
|
||||
switch decryptKey.Type {
|
||||
case passvault.RSARecord:
|
||||
// extract the aes key from the pubEncryptedKey
|
||||
aesKey, err = rsa.DecryptOAEP(sha1.New(), rand.Reader, &decryptKey.rsaKey, pubEncryptedKey, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case passvault.ECCRecord:
|
||||
// extract the aes key from the pubEncryptedKey
|
||||
aesKey, err = ecdh.Decrypt(decryptKey.eccKey, pubEncryptedKey)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
|
||||
// decrypt
|
||||
aesSession, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, encShare := range in {
|
||||
tmp := make([]byte, 16)
|
||||
aesSession.Decrypt(tmp, encShare)
|
||||
|
||||
out = append(out, tmp)
|
||||
}
|
||||
|
||||
cache.useKey(name, user, slot, labels)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user