Skip to content

Commit

Permalink
end-to-end: add encryption library
Browse files Browse the repository at this point in the history
Adds AES256-CTR+HMAC-SHA-512 en/decrpytion functions that wrap io.Reader.

Updates odeke-em#543
  • Loading branch information
sselph committed May 18, 2016
1 parent ce0d292 commit 704d7c5
Show file tree
Hide file tree
Showing 5 changed files with 524 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
sudo: false
language: go
go:
- 1.3
- 1.5
- tip
script:
go test -v -cover ./src
go test -v -cover ./src/...
101 changes: 101 additions & 0 deletions src/dcrypto/dcrypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package dcrypto provides end to end encryption for drive.

package dcrypto

import (
"bytes"
"encoding/binary"
"fmt"
"io"

"github.com/odeke-em/drive/src/dcrypto/v1"
)

// Version is the version of the en/decryption library used.
type Version uint32

// Decrypter is a function that creates a decrypter.
type Decrypter func(io.Reader, []byte) (io.ReadCloser, error)

// Encrypter is a function that creates a encrypter.
type Encrypter func(io.Reader, []byte) (io.Reader, error)

// These are the different versions of the en/decryption library.
const (
V1 Version = iota
)

// PreferedVersion is the prefered version of encryption.
const PreferedVersion = V1

var encrypters map[Version]Encrypter
var decrypters map[Version]Decrypter

func init() {
decrypters = map[Version]Decrypter{
V1: v1.NewDecryptReader,
}

encrypters = map[Version]Encrypter{
V1: v1.NewEncryptReader,
}
}

// NewEncrypter returns an Encrypter using the PreferedVersion.
func NewEncrypter(r io.Reader, password []byte) (io.Reader, error) {
v, err := writeVersion(PreferedVersion)
if err != nil {
return nil, err
}
encrypterFn, ok := encrypters[PreferedVersion]
if !ok {
return nil, fmt.Errorf("%v version could not be found", PreferedVersion)
}
encReader, err := encrypterFn(r, password)
if err != nil {
return nil, err
}
return io.MultiReader(bytes.NewBuffer(v), encReader), nil
}

// NewDecrypter returns a Decrypter based on the version used to encrypt.
func NewDecrypter(r io.Reader, password []byte) (io.ReadCloser, error) {
version, err := readVersion(r)
if err != nil {
return nil, err
}
decrypterFn, ok := decrypters[version]
if !ok {
return nil, fmt.Errorf("unknown decrypter for version(%d)", version)
}
return decrypterFn(r, password)
}

// writeVersion converts a Version to a []byte.
func writeVersion(i Version) ([]byte, error) {
buf := new(bytes.Buffer)
if err := binary.Write(buf, binary.LittleEndian, i); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

// readVersion reads and returns a Version from reader.
func readVersion(r io.Reader) (v Version, err error) {
err = binary.Read(r, binary.LittleEndian, &v)
return v, err
}
83 changes: 83 additions & 0 deletions src/dcrypto/dcrypto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dcrypto_test

import (
"bytes"
"crypto/rand"
"io/ioutil"
"testing"

"github.com/odeke-em/drive/src/dcrypto"
)

// randBytes returns random bytes in a byte slice of size.
func randBytes(size int) ([]byte, error) {
b := make([]byte, size)
_, err := rand.Read(b)
return b, err
}

// TestRoundTrip tests several size sets of data going through the encrypt/decrypt
// to make sure they come out the same.
func TestRoundTrip(t *testing.T) {
sizes := []int{0, 24, 1337, 66560}
spasswords := []string{
"",
"guest",
}
for _, x := range []int{13, 400} {
rp, err := randBytes(x)
if err != nil {
t.Fatalf("randBytes(%d) => err", x)
}
spasswords = append(spasswords, string(rp))
}
for _, spass := range spasswords {
password := []byte(spass)
for _, size := range sizes {
t.Logf("Testing file of size: %db", size)
b, err := randBytes(size)
if err != nil {
t.Errorf("randBytes(%d) => %q; want nil", size, err)
continue
}
encReader, err := dcrypto.NewEncrypter(bytes.NewBuffer(b), password)
if err != nil {
t.Errorf("NewEncrypter() => %q; want nil", err)
continue
}
cipher, err := ioutil.ReadAll(encReader)
if err != nil {
t.Errorf("ioutil.ReadAll(*Encrypter) => %q; want nil", err)
continue
}
decReader, err := dcrypto.NewDecrypter(bytes.NewBuffer(cipher), password)
if err != nil {
t.Errorf("NewDecrypter() => %q; want nil", err)
continue
}
plain, err := ioutil.ReadAll(decReader)
decReader.Close()
if err != nil {
t.Errorf("ioutil.ReadAll(*Decrypter) => %q; want nil", err)
continue
}
if !bytes.Equal(b, plain) {
t.Errorf("Encrypt/Decrypt of file size %d, resulted in different values", size)
}
}
}
}
Loading

0 comments on commit 704d7c5

Please sign in to comment.