removed support for per-objectID encryption

This commit is contained in:
Jarek Kowalski
2017-07-18 09:23:48 +02:00
parent 4a878738a0
commit 570dc0a897
4 changed files with 3 additions and 213 deletions

View File

@@ -6,7 +6,6 @@
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
@@ -81,33 +80,6 @@ func (fi *unencryptedFormat) Decrypt(cipherText []byte, oid ObjectID) ([]byte, e
return cipherText, nil
}
// Since we never share keys, using constant IV is fine.
// Instead of using all-zero, we use this one.
var constantIV = []byte("kopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopia")
// convergentEncryptionFormat implements encrypted format where encryption key is derived from the data and HMAC secret and IV is constant.
// By sharing HMAC secret alone, this allows multiple parties in posession of the same file to generate identical block IDs and encryption keys.
type convergentEncryptionFormat struct {
digestFunc digestFunction
createCipher func(key []byte) (cipher.Block, error)
keyBytes int
}
func (fi *convergentEncryptionFormat) ComputeObjectID(data []byte) ObjectID {
h := fi.digestFunc(data)
p := len(h) - fi.keyBytes
return ObjectID{StorageBlock: hex.EncodeToString(h[0:p]), EncryptionKey: h[p:]}
}
func (fi *convergentEncryptionFormat) Encrypt(plainText []byte, oid ObjectID) ([]byte, error) {
return symmetricEncrypt(fi.createCipher, oid.EncryptionKey, constantIV, plainText)
}
func (fi *convergentEncryptionFormat) Decrypt(cipherText []byte, oid ObjectID) ([]byte, error) {
return symmetricEncrypt(fi.createCipher, oid.EncryptionKey, constantIV, cipherText)
}
// syntheticIVEncryptionFormat implements encrypted format with single master AES key and StorageBlock==IV that's
// derived from HMAC-SHA256(content, secret).
type syntheticIVEncryptionFormat struct {
@@ -176,12 +148,6 @@ func init() {
"UNENCRYPTED_HMAC_SHA256_128": func(f *Format) (ObjectFormatter, error) {
return &unencryptedFormat{computeHMAC(sha256.New, f.Secret, 16)}, nil
},
"ENCRYPTED_HMAC_SHA512_384_AES256": func(f *Format) (ObjectFormatter, error) {
return &convergentEncryptionFormat{computeHMAC(sha512.New384, f.Secret, sha512.Size384), aes.NewCipher, 32}, nil
},
"ENCRYPTED_HMAC_SHA512_AES256": func(f *Format) (ObjectFormatter, error) {
return &convergentEncryptionFormat{computeHMAC(sha512.New, f.Secret, sha512.Size), aes.NewCipher, 32}, nil
},
"ENCRYPTED_HMAC_SHA256_AES256_SIV": func(f *Format) (ObjectFormatter, error) {
if len(f.MasterKey) < 32 {
return nil, fmt.Errorf("master key is not set")

View File

@@ -2,7 +2,6 @@
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"strconv"
@@ -33,16 +32,11 @@
// "D295754edeb35c17911b1fdf853f572fe" // storage block
// "I1,2c33acbcba3569f943d9e8aaea7817c5" // level-1 indirection block
// "I3,e18604fe53ee670558eb4234d2e55cb7" // level-3 indirection block
// "Daad048fd5721b43adaa353c407d23ff6.5617c50fb1d71b6f7a2c4c8bacacef1d2222eaa4b2245a3714686c658f8af3d9"
// // encrypted storage block with 256-bit key
// "I2,87381a8631dcc86256233437338e27c4.81cf86361dbc9b7905f12f6f6b80d7ec0edd487eeb339e1193805e3f58ef9505"
// // encrypted level-2 indirection block with 256-bit key
// "S30,50,D295754edeb35c17911b1fdf853f572fe" // section of "D295754edeb35c17911b1fdf853f572fe" between [30,80)
//
//
type ObjectID struct {
StorageBlock string
EncryptionKey []byte
Indirect int32
TextContent string
BinaryContent []byte
@@ -82,8 +76,6 @@ type HasObjectID interface {
// NullObjectID is the identifier of an null/empty object.
var NullObjectID ObjectID
const objectIDEncryptionInfoSeparator = "."
var (
inlineContentEncoding = base64.RawURLEncoding
)
@@ -93,17 +85,11 @@ type HasObjectID interface {
// Note that the object ID name often contains its encryption key, which is sensitive and can be quite long (~100 characters long).
func (oid ObjectID) String() string {
if oid.StorageBlock != "" {
var encryptionSuffix string
if len(oid.EncryptionKey) > 0 {
encryptionSuffix = "." + hex.EncodeToString(oid.EncryptionKey)
}
if oid.Indirect > 0 {
return fmt.Sprintf("I%v,%v%v", oid.Indirect, oid.StorageBlock, encryptionSuffix)
return fmt.Sprintf("I%v,%v", oid.Indirect, oid.StorageBlock)
}
return "D" + oid.StorageBlock + encryptionSuffix
return "D" + oid.StorageBlock
}
if oid.BinaryContent != nil {
@@ -151,11 +137,6 @@ func (oid *ObjectID) Validate() error {
return fmt.Errorf("indirect object without storage block: %+v", oid)
}
if len(oid.EncryptionKey) > 0 && len(oid.StorageBlock) == 0 {
return fmt.Errorf("encryption key without storage block: %+v", oid)
}
return nil
}
@@ -304,26 +285,7 @@ func ParseObjectID(s string) (ObjectID, error) {
}
}
firstSeparator := strings.Index(content, objectIDEncryptionInfoSeparator)
lastSeparator := strings.LastIndex(content, objectIDEncryptionInfoSeparator)
if firstSeparator == lastSeparator {
if firstSeparator == -1 {
// Found zero Separators in the ID - no encryption info.
return ObjectID{StorageBlock: content, Indirect: indirectLevel}, nil
}
if firstSeparator > 0 {
b, err := hex.DecodeString(content[firstSeparator+1:])
if err == nil && len(b) > 0 {
// Valid chunk ID with encryption info.
return ObjectID{
StorageBlock: content[0:firstSeparator],
EncryptionKey: b,
Indirect: indirectLevel,
}, nil
}
}
}
return ObjectID{StorageBlock: content, Indirect: indirectLevel}, nil
}
}

View File

@@ -1,7 +1,6 @@
package repo
import (
"bytes"
"reflect"
"strings"
"testing"
@@ -12,14 +11,6 @@ func TestParseMalformedObjectID(t *testing.T) {
"",
"B!$@#$!@#$",
"X",
"D.",
"D.x",
"D.af",
"Dx.ag",
"Dab.",
"Dab.a",
"Dab.abc",
"Dab.011",
"I.",
"I.x",
"I.af",
@@ -38,39 +29,3 @@ func TestParseMalformedObjectID(t *testing.T) {
}
}
}
func TestParseObjectIDEncryptionInfo(t *testing.T) {
cases := []struct {
objectID string
expected []byte
}{
{"B", nil},
{"BAQIDBA", nil},
{"Dabcdef", nil},
{"I1,abcdef", nil},
{"I2,abcdef", nil},
{"Dabcdef.00112233", []byte{0x00, 0x11, 0x22, 0x33}},
{"I1,abcdef.0011223344", []byte{0x00, 0x11, 0x22, 0x33, 0x44}},
{"I2,abcdef.0011223344", []byte{0x00, 0x11, 0x22, 0x33, 0x44}},
{"S1,2,Dabcdef.0011223344", nil},
{"S1,2,S3,4,Dabcdef.0011223344", nil},
}
for _, c := range cases {
objectID, err := ParseObjectID(c.objectID)
if err != nil {
t.Errorf("cannot parse object ID: %v", err)
continue
}
actual := objectID.EncryptionKey
if !bytes.Equal(actual, c.expected) {
t.Errorf("invalid encryption key for %v: %x, expected: %x", c.objectID, actual, c.expected)
}
uiString := objectID.String()
if uiString != c.objectID {
t.Errorf("invalid object ID string: %v: expected: %v", uiString, c.objectID)
}
}
}

View File

@@ -416,24 +416,6 @@ func TestFormats(t *testing.T) {
},
},
},
{
format: makeFormat("ENCRYPTED_HMAC_SHA512_384_AES256"),
oids: map[string]ObjectID{
"The quick brown fox jumps over the lazy dog": ObjectID{
StorageBlock: "d7f4727e2c0b39ae0f1e40cc96f60242",
EncryptionKey: mustParseBase16("d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237"),
},
},
},
{
format: makeFormat("ENCRYPTED_HMAC_SHA512_AES256"),
oids: map[string]ObjectID{
"The quick brown fox jumps over the lazy dog": ObjectID{
StorageBlock: "b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb",
EncryptionKey: mustParseBase16("82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"),
},
},
},
}
for caseIndex, c := range cases {
@@ -473,78 +455,3 @@ func TestFormats(t *testing.T) {
}
}
}
func TestInvalidEncryptionKey(t *testing.T) {
data := map[string][]byte{}
st := storagetesting.NewMapStorage(data)
format := Format{
Version: 1,
ObjectFormat: "ENCRYPTED_HMAC_SHA512_384_AES256",
Secret: []byte("key"),
MaxBlockSize: 1000,
}
repo, err := New(st, &format)
if err != nil {
t.Errorf("cannot create manager: %v", err)
return
}
bytesToWrite := make([]byte, 1024)
for i := range bytesToWrite {
bytesToWrite[i] = byte(i)
}
w := repo.NewWriter()
w.Write(bytesToWrite)
oid, err := w.Result(true)
if err != nil {
t.Errorf("error: %v", err)
}
rc, err := repo.Open(oid)
if err != nil || rc == nil {
t.Errorf("error opening valid ObjectID: %v", err)
return
}
// Key too short
rc, err = repo.Open(replaceEncryption(oid, oid.EncryptionKey[0:len(oid.EncryptionKey)-2]))
if err == nil || rc != nil {
t.Errorf("expected error when opening malformed object")
}
// Key too long
rc, err = repo.Open(replaceEncryption(oid, append(oid.EncryptionKey, 0xFF)))
if err == nil || rc != nil {
t.Errorf("expected error when opening malformed object")
}
// Invalid key
corruptedKey := append([]byte(nil), oid.EncryptionKey...)
corruptedKey[0]++
rc, err = repo.Open(replaceEncryption(oid, corruptedKey))
if err == nil || rc != nil {
t.Errorf("expected error when opening malformed object: %v", err)
}
// Now corrupt the data
data[oid.StorageBlock][0] ^= 1
rc, err = repo.Open(oid)
if err == nil || rc != nil {
t.Errorf("expected error when opening object with corrupt data")
}
}
func replaceEncryption(oid ObjectID, newEncryption []byte) ObjectID {
oid.EncryptionKey = newEncryption
return oid
}
func mustParseBase16(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex literal: " + s)
}
return b
}