From 91cd67f267e5c017f0df607a4e5caf3e3a484b1d Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Wed, 10 Sep 2014 09:22:04 -0700 Subject: [PATCH] Decrypt returns the list of users used for delegation When decrypting a chunk of data, red october will now report the users whose keys were used in the decryption. --- core/core.go | 22 +++++++++++++++++++--- core/core_test.go | 30 +++++++++++++++++++++++++----- cryptor/cryptor.go | 28 +++++++++++++++++++--------- index.html | 3 ++- redoctober.go | 6 ++++-- 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/core/core.go b/core/core.go index 14f4b01..e81cc93 100644 --- a/core/core.go +++ b/core/core.go @@ -8,10 +8,11 @@ import ( "encoding/json" "errors" "fmt" + "log" + "github.com/cloudflare/redoctober/cryptor" "github.com/cloudflare/redoctober/keycache" "github.com/cloudflare/redoctober/passvault" - "log" ) // Each of these structures corresponds to the JSON expected on the @@ -60,6 +61,11 @@ type decrypt struct { Data []byte } +type decryptWithDelegates struct { + Data []byte + Delegates []string +} + type modify struct { Name string Password string @@ -283,13 +289,23 @@ func Decrypt(jsonIn []byte) ([]byte, error) { return jsonStatusError(err) } - resp, err := cryptor.Decrypt(s.Data) + data, names, err := cryptor.Decrypt(s.Data) if err != nil { log.Println("Error decrypting:", err) return jsonStatusError(err) } - return jsonResponse(resp) + resp := &decryptWithDelegates{ + Data: data, + Delegates: names, + } + + out, err := json.Marshal(resp) + if err != nil { + return jsonStatusError(err) + } + + return jsonResponse(out) } // Modify processes a modify request. diff --git a/core/core_test.go b/core/core_test.go index 922b91e..03f38c8 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,10 +7,11 @@ package core import ( "bytes" "encoding/json" - "github.com/cloudflare/redoctober/keycache" - "github.com/cloudflare/redoctober/passvault" "os" "testing" + + "github.com/cloudflare/redoctober/keycache" + "github.com/cloudflare/redoctober/passvault" ) func TestCreate(t *testing.T) { @@ -449,8 +450,21 @@ func TestEncryptDecrypt(t *testing.T) { if s.Status != "ok" { t.Fatalf("Error in decrypt, %v", s.Status) } - if string(s.Response) != "Hello Jello" { - t.Fatalf("Error in decrypt, %v", string(s.Response)) + + var d decryptWithDelegates + err = json.Unmarshal(s.Response, &d) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + + if string(d.Data) != "Hello Jello" { + t.Fatalf("Error in decrypt, %v", string(d.Data)) + } + + if d.Delegates[0] != "Bob" && d.Delegates[1] != "Carol" { + if d.Delegates[1] != "Bob" && d.Delegates[0] != "Carol" { + t.Fatalf("Error in decrypt, %v", d.Delegates) + } } keycache.FlushCache() @@ -707,7 +721,13 @@ func TestStatic(t *testing.T) { t.Fatalf("Error in summary, %v", r.Status) } - if bytes.Compare(expected, r.Response) != 0 { + var d decryptWithDelegates + err = json.Unmarshal(r.Response, &d) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + + if bytes.Compare(expected, d.Data) != 0 { t.Fatalf("Error in summary, %v, %v", expected, r.Response) } diff --git a/cryptor/cryptor.go b/cryptor/cryptor.go index 8d260d8..5a77c56 100644 --- a/cryptor/cryptor.go +++ b/cryptor/cryptor.go @@ -12,12 +12,13 @@ import ( "crypto/sha1" "encoding/json" "errors" + "sort" + "strconv" + "github.com/cloudflare/redoctober/keycache" "github.com/cloudflare/redoctober/padding" "github.com/cloudflare/redoctober/passvault" "github.com/cloudflare/redoctober/symcrypt" - "sort" - "strconv" ) const ( @@ -111,15 +112,16 @@ func encryptKey(nameInner, nameOuter string, clearKey []byte, pubKeys map[string } // unwrapKey decrypts first key in keys whose encryption keys are in keycache -func unwrapKey(keys []MultiWrappedKey, pubKeys map[string]SingleWrappedKey) (unwrappedKey []byte, err error) { +func unwrapKey(keys []MultiWrappedKey, pubKeys map[string]SingleWrappedKey) (unwrappedKey []byte, names []string, err error) { var ( keyFound error fullMatch bool = false + nameSet = map[string]bool{} ) for _, mwKey := range keys { if err != nil { - return nil, err + return nil, nil, err } tmpKeyValue := mwKey.Key @@ -130,6 +132,7 @@ func unwrapKey(keys []MultiWrappedKey, pubKeys map[string]SingleWrappedKey) (unw if tmpKeyValue, keyFound = keycache.DecryptKey(tmpKeyValue, mwName, pubEncrypted.Key); keyFound != nil { break } + nameSet[mwName] = true } if keyFound == nil { fullMatch = true @@ -141,6 +144,12 @@ func unwrapKey(keys []MultiWrappedKey, pubKeys map[string]SingleWrappedKey) (unw 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 } @@ -339,14 +348,14 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) { } // Decrypt decrypts a file using the keys in the key cache. -func Decrypt(in []byte) (resp []byte, err error) { +func Decrypt(in []byte) (resp []byte, names []string, err error) { // unwrap encrypted file var encrypted EncryptedData if err = json.Unmarshal(in, &encrypted); err != nil { return } if encrypted.Version != DEFAULT_VERSION { - return nil, errors.New("Unknown version") + return nil, nil, errors.New("Unknown version") } // make sure file was encrypted with the active vault @@ -355,7 +364,7 @@ func Decrypt(in []byte) (resp []byte, err error) { return } if encrypted.VaultId != vaultId { - return nil, errors.New("Wrong vault") + return nil, nil, errors.New("Wrong vault") } // validate the size of the keys @@ -379,7 +388,7 @@ func Decrypt(in []byte) (resp []byte, err error) { // decrypt file key with delegate keys var unwrappedKey = make([]byte, 16) - if unwrappedKey, err = unwrapKey(encrypted.KeySet, encrypted.KeySetRSA); err != nil { + if unwrappedKey, names, err = unwrapKey(encrypted.KeySet, encrypted.KeySetRSA); err != nil { return } @@ -393,5 +402,6 @@ func Decrypt(in []byte) (resp []byte, err error) { // decrypt contents of file aesCBC.CryptBlocks(clearData, encrypted.Data) - return padding.RemovePadding(clearData) + resp, err = padding.RemovePadding(clearData) + return } diff --git a/index.html b/index.html index 2acb68c..60bb5cb 100644 --- a/index.html +++ b/index.html @@ -403,7 +403,8 @@ submit( $form, { data : data, success : function(d){ - $form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '

Successfully decrypted data:

'+ window.atob(d.Response)+'
' }) ); + d = JSON.parse(window.atob(d.Response)); + $form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '

Successfully decrypted data:

'+ window.atob(d.Data)+'

Delegates: '+d.Delegates.sort().join(', ')+'

' }) ); } }); }); diff --git a/redoctober.go b/redoctober.go index e5c18e1..300fbf9 100644 --- a/redoctober.go +++ b/redoctober.go @@ -12,7 +12,6 @@ import ( "encoding/pem" "flag" "fmt" - "github.com/cloudflare/redoctober/core" "io" "io/ioutil" "log" @@ -21,6 +20,8 @@ import ( "os" "runtime" "time" + + "github.com/cloudflare/redoctober/core" ) // List of URLs to register and their related functions @@ -633,7 +634,8 @@ var indexHtml = []byte(` submit( $form, { data : data, success : function(d){ - $form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '

Successfully decrypted data:

'+ window.atob(d.Response)+'
' }) ); + d = JSON.parse(window.atob(d.Response)); + $form.find('.feedback').empty().append( makeAlert({ type: 'success', message: '

Successfully decrypted data:

'+ window.atob(d.Data)+'

Delegates: '+d.Delegates.sort().join(', ')+'

' }) ); } }); });