Cleaned up format.go to eliminate confusing crypto, removed non-HMAC option.

Removed fold() operation in favor of truncation
This commit is contained in:
Jarek Kowalski
2016-09-28 18:46:13 -07:00
parent cb7e438a04
commit fba5d5bc0b
2 changed files with 30 additions and 62 deletions

View File

@@ -55,18 +55,16 @@ type ObjectFormatter interface {
Decrypt(cipherText []byte, oid ObjectID) ([]byte, error)
}
// digestFunction computes the digest (hash, optionally HMAC) of a given block of bytes.
type digestFunction func([]byte) []byte
// unencryptedFormat implements non-encrypted format.
type unencryptedFormat struct {
hashCtor func() hash.Hash
fold int
secret []byte
digestFunc digestFunction
}
func (fi *unencryptedFormat) ComputeObjectID(data []byte) ObjectID {
h := hashContent(fi.hashCtor, data, fi.secret)
if fi.fold > 0 {
h = fold(h, fi.fold)
}
h := fi.digestFunc(data)
return ObjectID{StorageBlock: hex.EncodeToString(h)}
}
@@ -86,14 +84,13 @@ func (fi *unencryptedFormat) Decrypt(cipherText []byte, oid ObjectID) ([]byte, e
// 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 {
hashCtor func() hash.Hash
digestFunc digestFunction
createCipher func(key []byte) (cipher.Block, error)
keyBytes int
secret []byte
}
func (fi *convergentEncryptionFormat) ComputeObjectID(data []byte) ObjectID {
h := hashContent(fi.hashCtor, data, fi.secret)
h := fi.digestFunc(data)
p := len(h) - fi.keyBytes
return ObjectID{StorageBlock: hex.EncodeToString(h[0:p]), EncryptionKey: h[p:]}
@@ -110,20 +107,13 @@ func (fi *convergentEncryptionFormat) Decrypt(cipherText []byte, oid ObjectID) (
// syntheticIVEncryptionFormat implements encrypted format with single master AES key and StorageBlock==IV that's
// derived from HMAC-SHA256(content, secret).
type syntheticIVEncryptionFormat struct {
digestFunc digestFunction
createCipher func(key []byte) (cipher.Block, error)
aesKey []byte
hmacSecret []byte
}
func (fi *syntheticIVEncryptionFormat) ComputeObjectID(data []byte) ObjectID {
h := hashContent(sha256.New, data, fi.hmacSecret)
// Fold the bits into 16 bytes required for the IV.
for i := aes.BlockSize; i < len(h); i++ {
h[i%aes.BlockSize] ^= h[i]
}
h = h[0:aes.BlockSize]
h := fi.digestFunc(data)
return ObjectID{StorageBlock: hex.EncodeToString(h)}
}
@@ -174,25 +164,25 @@ func decodeHexSuffix(s string, length int) ([]byte, error) {
func init() {
SupportedFormats = map[string]func(f *Format) (ObjectFormatter, error){
"TESTONLY_MD5": func(f *Format) (ObjectFormatter, error) {
return &unencryptedFormat{md5.New, 0, f.Secret}, nil
return &unencryptedFormat{computeHash(md5.New, md5.Size)}, nil
},
"UNENCRYPTED_HMAC_SHA256": func(f *Format) (ObjectFormatter, error) {
return &unencryptedFormat{sha256.New, 0, f.Secret}, nil
return &unencryptedFormat{computeHMAC(sha256.New, f.Secret, sha256.Size)}, nil
},
"UNENCRYPTED_HMAC_SHA256_128": func(f *Format) (ObjectFormatter, error) {
return &unencryptedFormat{sha256.New, 16, f.Secret}, nil
return &unencryptedFormat{computeHMAC(sha256.New, f.Secret, 16)}, nil
},
"ENCRYPTED_HMAC_SHA512_384_AES256": func(f *Format) (ObjectFormatter, error) {
return &convergentEncryptionFormat{sha512.New384, aes.NewCipher, 32, f.Secret}, nil
return &convergentEncryptionFormat{computeHMAC(sha512.New384, f.Secret, sha512.Size384), aes.NewCipher, 32}, nil
},
"ENCRYPTED_HMAC_SHA512_AES256": func(f *Format) (ObjectFormatter, error) {
return &convergentEncryptionFormat{sha512.New, aes.NewCipher, 32, f.Secret}, nil
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")
}
return &syntheticIVEncryptionFormat{aes.NewCipher, f.MasterKey, f.Secret}, nil
return &syntheticIVEncryptionFormat{computeHMAC(sha256.New, f.Secret, aes.BlockSize), aes.NewCipher, f.MasterKey}, nil
},
}
}
@@ -200,25 +190,20 @@ func init() {
// DefaultObjectFormat is the format that should be used by default when creating new repositories.
var DefaultObjectFormat = "ENCRYPTED_HMAC_SHA256_AES256_SIV"
func fold(b []byte, size int) []byte {
if len(b) == size {
return b
// computeHash returns a digestFunction that computes a hash of a given block of bytes and truncates results to the given size.
func computeHash(hf func() hash.Hash, truncate int) digestFunction {
return func(b []byte) []byte {
h := hf()
h.Write(b)
return h.Sum(nil)[0:truncate]
}
for i := size; i < len(b); i++ {
b[i%size] ^= b[i]
}
return b[0:size]
}
func hashContent(hf func() hash.Hash, data []byte, secret []byte) []byte {
var h hash.Hash
if len(secret) > 0 {
h = hmac.New(hf, secret)
} else {
h = hf()
// computeHMAC returns a digestFunction that computes HMAC(hash, secret) of a given block of bytes and truncates results to the given size.
func computeHMAC(hf func() hash.Hash, secret []byte, truncate int) digestFunction {
return func(b []byte) []byte {
h := hmac.New(hf, secret)
h.Write(b)
return h.Sum(nil)[0:truncate]
}
h.Write(data)
return h.Sum(nil)
}

View File

@@ -201,7 +201,6 @@ func TestHMAC(t *testing.T) {
s := testFormat()
s.ObjectFormat = "TESTONLY_MD5"
s.Secret = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}
repo, err := New(storagetesting.NewMapStorage(data), s)
if err != nil {
@@ -210,8 +209,8 @@ func TestHMAC(t *testing.T) {
w := repo.NewWriter()
w.Write(content)
result, err := w.Result(false)
if result.String() != "D697eaf0aca3a3aea3a75164746ffaa79" {
t.Errorf("unexpected result: %v err: %v", result, err)
if result.String() != "D999732b72ceff665b3f7608411db66a4" {
t.Errorf("unexpected result: %v err: %v", result.String(), err)
}
}
@@ -378,19 +377,11 @@ func TestFormats(t *testing.T) {
format *Format
oids map[string]ObjectID
}{
{
format: makeFormat("TESTONLY_MD5"), // MD5-HMAC with secret 'key'
oids: map[string]ObjectID{
"": ObjectID{StorageBlock: "63530468a04e386459855da0063b6596"},
"The quick brown fox jumps over the lazy dog": ObjectID{StorageBlock: "80070713463e7749b90c2dc24911e275"},
},
},
{
format: &Format{
Version: 1,
ObjectFormat: "TESTONLY_MD5",
MaxBlockSize: 10000,
Secret: nil, // non-HMAC version
},
oids: map[string]ObjectID{
"": ObjectID{StorageBlock: "d41d8cd98f00b204e9800998ecf8427e"},
@@ -399,14 +390,6 @@ func TestFormats(t *testing.T) {
},
},
},
{
format: makeFormat("TESTONLY_MD5"),
oids: map[string]ObjectID{
"The quick brown fox jumps over the lazy dog": ObjectID{
StorageBlock: "80070713463e7749b90c2dc24911e275",
},
},
},
{
format: makeFormat("UNENCRYPTED_HMAC_SHA256"),
oids: map[string]ObjectID{
@@ -419,7 +402,7 @@ func TestFormats(t *testing.T) {
format: makeFormat("UNENCRYPTED_HMAC_SHA256_128"),
oids: map[string]ObjectID{
"The quick brown fox jumps over the lazy dog": ObjectID{
StorageBlock: "18f1da557915937d2675055a87758d9b",
StorageBlock: "f7bc83f430538424b13298e6aa6fb143",
},
},
},