Skip to content
Permalink
Browse files

Update to pass KeyContainer instead of Key array

Passing Key array resulted in situations where the Key array would
suddendly appear blank; this was presumably due to memguard's
protection of those bytes.

Now, Key arrays are extracted only when reaching a third-party
package that requires a Key array.
  • Loading branch information
jonathan-robertson committed Dec 8, 2017
1 parent 00d45f7 commit ed3f9cc9527181ae6067b55fb59b376780ce0323
Showing with 44 additions and 45 deletions.
  1. +20 −12 cloud/cloud.go
  2. +6 −15 cloud/cloud_test.go
  3. +8 −8 secure/secure.go
  4. +6 −6 secure/secure_test.go
  5. +4 −4 stream/encrypt.go
@@ -5,6 +5,8 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"io"
"os"
"time"

"github.com/jonathan-robertson/lockedarchive/secure"
@@ -23,9 +25,9 @@ type Client interface {

List(chan Entry) error

Upload(Entry) error
Upload(Entry, *os.File) error
Head(Entry) error
Download(Entry) error
Download(Entry) (io.ReadCloser, error)
Update(Entry) error
Delete(Entry) error

@@ -37,19 +39,22 @@ type Client interface {
type Entry struct {
ID string `json:"-"` // ID representing this Entry

Key string `json:"k"` // Encrypted encryption key used to encrypt/decrypt this data
ParentID string `json:"p"` // ID representing Entry containing this one
Name string `json:"n"` // Name of this Entry
IsDir bool `json:"d"` // Whether or not this Entry contains others
Size int64 `json:"s"` // Size of Entry's data
LastModified time.Time `json:"m"` // Last time Entry was updated
Key string `json:"k"` // Encrypted encryption key used to encrypt/decrypt this data
ParentID string `json:"p"` // ID representing Entry containing this one
Name string `json:"n"` // Name of this Entry
IsDir bool `json:"d"` // Whether or not this Entry contains others
Size int64 `json:"s"` // Size of Entry's data
LastModified time.Time `json:"m"` // Last time Entry was updated
Mode os.FileMode `json:"f"` // File Mode

// TODO: add these in later
// Tags []string
}

// Meta returns Entry's encrypted metadata
func (entry Entry) Meta(key secure.Key) (encryptedMeta string, err error) {
func (entry Entry) Meta(kc *secure.KeyContainer) (encryptedMeta string, err error) {

// TODO: update to decrypt entry.Key with incoming key

plaintext, err := json.Marshal(entry)
if err != nil {
@@ -61,20 +66,23 @@ func (entry Entry) Meta(key secure.Key) (encryptedMeta string, err error) {
return
}

ciphertext := secure.EncryptAndWipe(key, nonce, plaintext)
ciphertext := secure.EncryptAndWipe(kc, nonce, plaintext)

return base64.StdEncoding.EncodeToString(ciphertext), err
}

// UpdateMeta reads in encrypted metadata and translates it to Entry's fields
// TODO: Update to no longer receive key - pull it from config
func (entry *Entry) UpdateMeta(encryptedMeta string, key secure.Key) error {
func (entry *Entry) UpdateMeta(encryptedMeta string, kc *secure.KeyContainer) error {

// TODO: update to decrypt entry.Key with incoming key

decoded, err := base64.StdEncoding.DecodeString(encryptedMeta)
if err != nil {
return err
}

plaintext, err := secure.Decrypt(key, decoded)
plaintext, err := secure.Decrypt(kc, decoded)
if err != nil {
return err
}
@@ -1,32 +1,23 @@
package cloud
package cloud_test

import (
"testing"
"time"

"github.com/jonathan-robertson/lockedarchive/stream"
"github.com/jonathan-robertson/lockedarchive/cloud"
)

func TestEntry(t *testing.T) {
entry := Entry{
entry := cloud.Entry{
Key: "123456789",
ParentKey: "987654321",
Name: "Important.doc",
IsDir: false,
Size: 153432,
LastModified: time.Now(),
Mode: nil,
}

key := stream.GenerateKey()
meta, err := entry.Meta(key)
if err != nil {
t.Fatal(err)
}
t.Logf("encoded:\n%s\n%x", meta, meta)

var newEntry Entry
if err := newEntry.UpdateMeta(meta, key); err != nil {
t.Fatal(err)
}
t.Logf("success: %+v", newEntry)
t.Fail("not implemented")
// TODO: need to finish deciding on how to encrypt/decrypt entry.Key first
}
@@ -134,29 +134,29 @@ func IncrementNonce(nonce Nonce) {

// Encrypt encrypts the input using NaCl's secretbox package and the nonce is prepended to the ciphertext.
// A sealed message will the same size as the original message plus secretbox.Overhead bytes long.
func Encrypt(key Key, nonce Nonce, message []byte) []byte {
func Encrypt(kc *KeyContainer, nonce Nonce, message []byte) []byte {
out := make([]byte, NonceSize)
copy(out, nonce[:])
return secretbox.Seal(out, message, nonce, key)
return secretbox.Seal(out, message, nonce, kc.Key())
}

// EncryptAndWipe performs the same steps as Encrypt, but also wipes the message.
// The slice is wiped once the bytes have been encrypted.
func EncryptAndWipe(key Key, nonce Nonce, message []byte) []byte {
func EncryptAndWipe(kc *KeyContainer, nonce Nonce, message []byte) []byte {
defer Wipe(message) // zero bytes of original message in memory asap
return Encrypt(key, nonce, message)
return Encrypt(kc, nonce, message)
}

// Decrypt extracts the nonce from the ciphertext, and attempts to decrypt with secretbox.
func Decrypt(key Key, message []byte) ([]byte, error) {
func Decrypt(kc *KeyContainer, message []byte) ([]byte, error) {
if len(message) < (NonceSize + secretbox.Overhead) {
return nil, ErrDecrypt
}

nonce := new([NonceSize]byte)
copy(nonce[:], message[:NonceSize])

out, ok := secretbox.Open(nil, message[NonceSize:], nonce, key)
out, ok := secretbox.Open(nil, message[NonceSize:], nonce, kc.Key())
if !ok {
return nil, ErrDecrypt
}
@@ -180,7 +180,7 @@ func EncryptWithSalt(pc *PassphraseContainer, nonce Nonce, message []byte) ([]by
return nil, err
}

encryptedData := Encrypt(kc.Key(), nonce, message)
encryptedData := Encrypt(kc, nonce, message)

contents := append(salt[:], encryptedData...)
return contents, nil
@@ -221,7 +221,7 @@ func DecryptWithSalt(pc *PassphraseContainer, message []byte) ([]byte, error) {
return nil, err
}

return Decrypt(kc.Key(), message[SaltSize:])
return Decrypt(kc, message[SaltSize:])
}

// DecryptWithSaltFromStringToKey decrypts a base64-encoded key and returns it as a KeyContainer
@@ -42,8 +42,8 @@ func TestEncrypt(t *testing.T) {

plaintext := []byte("string to test")

encrypted := encrypt(t, kc.Key(), plaintext)
decrypted := decrypt(t, kc.Key(), encrypted)
encrypted := encrypt(t, kc, plaintext)
decrypted := decrypt(t, kc, encrypted)
assertBytesEqual(t, plaintext, decrypted)

t.Log("data encrypted and decrypted to get same result")
@@ -86,12 +86,12 @@ func assertBytesEqual(t *testing.T, x, y []byte) {
}
}

func encrypt(t *testing.T, key secure.Key, plaintext []byte) []byte {
return secure.Encrypt(key, makeNonce(t), plaintext)
func encrypt(t *testing.T, kc *secure.KeyContainer, plaintext []byte) []byte {
return secure.Encrypt(kc, makeNonce(t), plaintext)
}

func decrypt(t *testing.T, key secure.Key, ciphertext []byte) []byte {
decrypted, err := secure.Decrypt(key, ciphertext)
func decrypt(t *testing.T, kc *secure.KeyContainer, ciphertext []byte) []byte {
decrypted, err := secure.Decrypt(kc, ciphertext)
if err != nil {
t.Fatal(err)
}
@@ -30,7 +30,7 @@ var (

// Encrypt encrypts a stream of data in chunks.
// IF SIZE IS KNOWN, caller should first use TooLargeToChunk
func Encrypt(ctx context.Context, key secure.Key, r io.Reader, w io.Writer) (int64, error) {
func Encrypt(ctx context.Context, kc *secure.KeyContainer, r io.Reader, w io.Writer) (int64, error) {
nonce, err := secure.GenerateNonce()
if err != nil {
return 0, err
@@ -61,7 +61,7 @@ func Encrypt(ctx context.Context, key secure.Key, r io.Reader, w io.Writer) (int
}

if length > 0 {
encryptedChunk := secure.EncryptAndWipe(key, nonce, chunk[:length])
encryptedChunk := secure.EncryptAndWipe(kc, nonce, chunk[:length])

bytesWritten, writeErr := w.Write(encryptedChunk)
if writeErr != nil {
@@ -78,7 +78,7 @@ func Encrypt(ctx context.Context, key secure.Key, r io.Reader, w io.Writer) (int
}

// Decrypt decrypts a stream of data in chunks
func Decrypt(ctx context.Context, key secure.Key, r io.Reader, w io.Writer) (int64, error) {
func Decrypt(ctx context.Context, kc *secure.KeyContainer, r io.Reader, w io.Writer) (int64, error) {
var (
chunk = make([]byte, DecryptionChunkSize)
written int64
@@ -96,7 +96,7 @@ func Decrypt(ctx context.Context, key secure.Key, r io.Reader, w io.Writer) (int
}

if length > 0 {
decryptedChunk, encErr := secure.Decrypt(key, chunk[:length])
decryptedChunk, encErr := secure.Decrypt(kc, chunk[:length])
if encErr != nil {
return 0, encErr
}

0 comments on commit ed3f9cc

Please sign in to comment.
You can’t perform that action at this time.