From 6812f3a9cd33d08034bbb5abea3969a24ab2194b Mon Sep 17 00:00:00 2001 From: Jarek Kowalski Date: Thu, 19 May 2016 19:56:36 -0700 Subject: [PATCH] tweaked object ID format --- cmd/kopia/command_create.go | 2 +- repo/format.go | 153 +++++++++++++++++++++--------------- repo/object_writer.go | 7 +- repo/objectid.go | 30 ++++--- repo/objectid_test.go | 32 ++++---- repo/repository.go | 98 +++++++---------------- repo/repository_test.go | 55 ++++++++----- 7 files changed, 194 insertions(+), 183 deletions(-) diff --git a/cmd/kopia/command_create.go b/cmd/kopia/command_create.go index 9961d9a9b..5bc1f6f7d 100644 --- a/cmd/kopia/command_create.go +++ b/cmd/kopia/command_create.go @@ -18,7 +18,7 @@ createMaxBlobSize = createCommand.Flag("max-blob-size", "Maximum size of a data chunk in bytes.").Default("20000000").Int() createInlineBlobSize = createCommand.Flag("inline-blob-size", "Maximum size of an inline data chunk in bytes.").Default("32768").Int() createVaultEncryptionFormat = createCommand.Flag("vault-encryption", "Vault encryption format").Default("aes-256").Enum(supportedVaultEncryptionFormats()...) - createObjectFormat = createCommand.Flag("object-format", "Specifies custom object format to be used").Default("hmac-sha256").Enum(supportedObjectFormats()...) + createObjectFormat = createCommand.Flag("object-format", "Specifies custom object format to be used").Default("sha256t128-aes256").Enum(supportedObjectFormats()...) createOverwrite = createCommand.Flag("overwrite", "Overwrite existing data.").Bool() ) diff --git a/repo/format.go b/repo/format.go index 09bafd3e8..2b8c3a1e5 100644 --- a/repo/format.go +++ b/repo/format.go @@ -3,11 +3,20 @@ import ( "crypto/aes" "crypto/cipher" + "crypto/hmac" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "hash" + "io" + + "golang.org/x/crypto/hkdf" +) + +var ( + hkdfInfoBlockID = []byte("BlockID") + hkdfInfoCryptoKey = []byte("CryptoKey") ) // Format describes the format of object data. @@ -21,46 +30,11 @@ type Format struct { // ObjectIDFormat describes single format ObjectID type ObjectIDFormat struct { - Name string + Name string + IsEncrypted bool - hashFuncMaker func(secret []byte) func() hash.Hash - createCipher func([]byte) (cipher.Block, error) - keygen keygenFunc -} - -// IsEncrypted determines whether the ObjectIDFormat is encrypted. -func (oif *ObjectIDFormat) IsEncrypted() bool { - return oif.createCipher != nil -} - -// HashSizeBits returns the number of bits returned by hash function. -func (oif *ObjectIDFormat) HashSizeBits() int { - hf := oif.hashFuncMaker(nil) - return hf().Size() * 8 -} - -// BlockIDLength returns the number of characters in a stored block ID. -func (oif *ObjectIDFormat) BlockIDLength() int { - hf := oif.hashFuncMaker(nil) - if oif.keygen == nil { - return hf().Size() * 2 - } - - h := hf().Sum(nil) - blockID, _ := oif.keygen(h) - return len(blockID) * 2 -} - -// EncryptionKeySizeBits returns the size of encryption key in bits, -// or zero if no encryption is used in this format. -func (oif *ObjectIDFormat) EncryptionKeySizeBits() int { - if oif.keygen == nil { - return 0 - } - hf := oif.hashFuncMaker(nil) - h := hf().Sum(nil) - _, key := oif.keygen(h) - return len(key) * 8 + hashBuffer func(data []byte, secret []byte) ([]byte, []byte) + createCipher func(key []byte) (cipher.Block, error) } // ObjectIDFormats is a collection of ObjectIDFormat @@ -77,27 +51,82 @@ func (fmts ObjectIDFormats) Find(name string) *ObjectIDFormat { return nil } -// SupportedFormats contains supported repository formats. -var SupportedFormats = ObjectIDFormats{ - // Non-encrypted formats. - &ObjectIDFormat{"md5", nonHMAC(md5.New), nil, nil}, - &ObjectIDFormat{"hmac-md5", withHMAC(md5.New), nil, nil}, - &ObjectIDFormat{"sha1", nonHMAC(sha1.New), nil, nil}, - &ObjectIDFormat{"hmac-sha1", withHMAC(sha1.New), nil, nil}, - &ObjectIDFormat{"sha256", nonHMAC(sha256.New), nil, nil}, - &ObjectIDFormat{"hmac-sha256", withHMAC(sha256.New), nil, nil}, - &ObjectIDFormat{"sha512-256", nonHMAC(sha512.New512_256), nil, nil}, - &ObjectIDFormat{"hmac-sha512-256", withHMAC(sha512.New512_256), nil, nil}, - &ObjectIDFormat{"sha512", nonHMAC(sha512.New), nil, nil}, - &ObjectIDFormat{"hmac-sha512", withHMAC(sha512.New), nil, nil}, - &ObjectIDFormat{"sha256-trunc128", nonHMAC(sha256.New), nil, splitKeyGenerator(16, 0)}, - &ObjectIDFormat{"hmac-sha256-trunc128", withHMAC(sha256.New), nil, splitKeyGenerator(16, 0)}, - &ObjectIDFormat{"sha512-trunc128", nonHMAC(sha512.New), nil, splitKeyGenerator(16, 0)}, - &ObjectIDFormat{"hmac-sha512-trunc128", withHMAC(sha512.New), nil, splitKeyGenerator(16, 0)}, - - // Encrypted formats - &ObjectIDFormat{"hmac-sha512-aes256", withHMAC(sha512.New), aes.NewCipher, splitKeyGenerator(32, 32)}, - &ObjectIDFormat{"hmac-sha384-aes256", withHMAC(sha512.New384), aes.NewCipher, splitKeyGenerator(16, 32)}, - &ObjectIDFormat{"hmac-sha256-aes128", withHMAC(sha256.New), aes.NewCipher, splitKeyGenerator(16, 16)}, - &ObjectIDFormat{"hmac-sha512-256-aes128", withHMAC(sha512.New512_256), aes.NewCipher, splitKeyGenerator(16, 16)}, +func nonEncryptedFormat(name string, hf func() hash.Hash, hashSize int) *ObjectIDFormat { + return &ObjectIDFormat{ + Name: name, + hashBuffer: func(data []byte, secret []byte) ([]byte, []byte) { + return hashContent(hf, data, secret)[0:hashSize], nil + }, + } } + +func hashContent(hf func() hash.Hash, data []byte, secret []byte) []byte { + var h hash.Hash + + if secret != nil { + h = hmac.New(hf, secret) + } else { + h = hf() + } + h.Write(data) + return h.Sum(nil) +} + +func encryptedFormat( + name string, + hf func() hash.Hash, + blockIDSize int, + createCipher func(key []byte) (cipher.Block, error), + keySize int, +) *ObjectIDFormat { + return &ObjectIDFormat{ + Name: name, + hashBuffer: func(data []byte, secret []byte) ([]byte, []byte) { + contentHash := hashContent(sha512.New, data, secret) + + s1 := hkdf.New(sha256.New, contentHash, nil, hkdfInfoBlockID) + blockID := make([]byte, blockIDSize) + io.ReadFull(s1, blockID) + + s2 := hkdf.New(sha256.New, contentHash, nil, hkdfInfoCryptoKey) + cryptoKey := make([]byte, keySize) + io.ReadFull(s2, cryptoKey) + + return blockID, cryptoKey + }, + createCipher: createCipher, + } +} + +func buildObjectIDFormats() ObjectIDFormats { + var result ObjectIDFormats + + for _, h := range []struct { + name string + hash func() hash.Hash + hashSize int + }{ + {"md5", md5.New, md5.Size}, + {"sha1", sha1.New, sha1.Size}, + {"sha224", sha256.New224, sha256.Size224}, + {"sha256", sha256.New, sha256.Size}, + {"sha256t128", sha256.New, 16}, + {"sha256t160", sha256.New, 20}, + {"sha384", sha512.New384, sha512.Size384}, + {"sha512t128", sha512.New, 16}, + {"sha512t160", sha512.New, 20}, + {"sha512-224", sha512.New512_224, sha512.Size224}, + {"sha512-256", sha512.New512_256, sha512.Size256}, + {"sha512", sha512.New, sha512.Size}, + } { + result = append(result, nonEncryptedFormat(h.name, h.hash, h.hashSize)) + result = append(result, encryptedFormat(h.name+"-aes128", h.hash, h.hashSize, aes.NewCipher, 16)) + result = append(result, encryptedFormat(h.name+"-aes192", h.hash, h.hashSize, aes.NewCipher, 24)) + result = append(result, encryptedFormat(h.name+"-aes256", h.hash, h.hashSize, aes.NewCipher, 32)) + } + + return result +} + +// SupportedFormats contains supported repository formats. +var SupportedFormats = buildObjectIDFormats() diff --git a/repo/object_writer.go b/repo/object_writer.go index cbaec7558..b00d8d23d 100644 --- a/repo/object_writer.go +++ b/repo/object_writer.go @@ -97,7 +97,10 @@ func (w *objectWriter) flushBuffer(force bool) error { length = w.buffer.Len() } - objectID, readCloser := w.mgr.hashBufferForWriting(w.buffer, string(w.objectType)+w.prefix) + objectID, readCloser, err := w.mgr.hashBufferForWriting(w.buffer, string(w.objectType)+w.prefix) + if err != nil { + return err + } w.buffer = nil if err := w.mgr.storage.PutBlock(objectID.BlockID(), readCloser, blob.PutOptions{}); err != nil { @@ -136,7 +139,7 @@ func (w *objectWriter) Result(forceStored bool) (ObjectID, error) { return "B", nil } - if w.buffer.Len() < w.mgr.maxInlineBlobSize { + if w.buffer.Len() < w.mgr.format.MaxInlineBlobSize { return NewInlineObjectID(w.buffer.Bytes()), nil } } diff --git a/repo/objectid.go b/repo/objectid.go index 22cda4136..b58cea89b 100644 --- a/repo/objectid.go +++ b/repo/objectid.go @@ -12,6 +12,10 @@ binaryEncoding = base64.RawURLEncoding ) +const ( + objectIDEncryptionInfoSeparator = "." +) + // ObjectIDType describes the type of the chunk. type ObjectIDType string @@ -81,9 +85,9 @@ func (c ObjectID) InlineData() []byte { func (c ObjectID) BlockID() string { if c.Type().IsStored() { content := string(c[1:]) - firstColon := strings.Index(content, ":") - if firstColon > 0 { - return string(content[0:firstColon]) + firstSeparator := strings.Index(content, objectIDEncryptionInfoSeparator) + if firstSeparator > 0 { + return string(content[0:firstSeparator]) } return string(content) @@ -96,9 +100,9 @@ func (c ObjectID) BlockID() string { func (c ObjectID) EncryptionInfo() ObjectEncryptionInfo { if c.Type().IsStored() { content := string(c[1:]) - firstColon := strings.Index(content, ":") - if firstColon > 0 { - return ObjectEncryptionInfo(content[firstColon+1:]) + firstSeparator := strings.Index(content, objectIDEncryptionInfoSeparator) + if firstSeparator > 0 { + return ObjectEncryptionInfo(content[firstSeparator+1:]) } } @@ -137,16 +141,16 @@ func ParseObjectID(objectIDString string) (ObjectID, error) { } case ObjectIDTypeList, ObjectIDTypeStored: - firstColon := strings.Index(content, ":") - lastColon := strings.LastIndex(content, ":") - if firstColon == lastColon { - if firstColon == -1 { - // Found zero colons in the ID - no encryption info. + 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(objectIDString), nil } - if firstColon > 0 { - b, err := hex.DecodeString(content[firstColon+1:]) + if firstSeparator > 0 { + b, err := hex.DecodeString(content[firstSeparator+1:]) if err == nil && len(b) > 0 && len(b)%2 == 0 { // Valid chunk ID with encryption info. return ObjectID(objectIDString), nil diff --git a/repo/objectid_test.go b/repo/objectid_test.go index 46dffcd88..bb53ecfaa 100644 --- a/repo/objectid_test.go +++ b/repo/objectid_test.go @@ -10,20 +10,20 @@ func TestParseMalformedObjectID(t *testing.T) { "", "B!$@#$!@#$", "X", - "D:", - "D:x", - "D:af", - "Dx:ag", - "Dab:", - "Dab:a", - "Dab:abc", - "Dab:00", - "Dab:011", - "L:", - "L:x", - "L:af", - "Lx:ag", - "Lab:", + "D.", + "D.x", + "D.af", + "Dx.ag", + "Dab.", + "Dab.a", + "Dab.abc", + "Dab.00", + "Dab.011", + "L.", + "L.x", + "L.af", + "Lx.ag", + "Lab.", "Xsomething", } @@ -49,11 +49,11 @@ func TestParseObjectIDEncryptionInfo(t *testing.T) { {"Dabcdef", NoEncryption}, {"Labcdef", NoEncryption}, { - "Dabcdef:00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", + "Dabcdef.00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", ObjectEncryptionInfo("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"), }, { - "Labcdef:00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", + "Labcdef.00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", ObjectEncryptionInfo("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"), }, } diff --git a/repo/repository.go b/repo/repository.go index 040b2a774..b23820afc 100644 --- a/repo/repository.go +++ b/repo/repository.go @@ -11,7 +11,6 @@ "hash" "io" "io/ioutil" - "log" "strconv" "strings" "sync/atomic" @@ -27,20 +26,6 @@ // Instead of using all-zero, we use this one. var constantIV = []byte("kopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopiakopia") -func nonHMAC(hf func() hash.Hash) func(secret []byte) func() hash.Hash { - return func(secret []byte) func() hash.Hash { - return hf - } -} - -func withHMAC(hf func() hash.Hash) func(secret []byte) func() hash.Hash { - return func(key []byte) func() hash.Hash { - return func() hash.Hash { - return hmac.New(hf, key) - } - } -} - // Repository objects addressed by their content allows reading and writing them. type Repository interface { // NewWriter opens an ObjectWriter for writing new content to the blob. @@ -83,12 +68,8 @@ type repository struct { bufferManager *bufferManager stats RepositoryStats - maxInlineBlobSize int - maxBlobSize int - - hashFunc func() hash.Hash - createCipher func([]byte) (cipher.Block, error) - keygen keygenFunc + format Format + idFormat ObjectIDFormat } func (mgr *repository) Close() { @@ -200,24 +181,18 @@ func NewRepository( if f.MaxBlobSize < 100 { return nil, fmt.Errorf("MaxBlobSize is not set") } - mgr := &repository{ - storage: r, - maxInlineBlobSize: f.MaxInlineBlobSize, - maxBlobSize: f.MaxBlobSize, - } - - if mgr.maxBlobSize == 0 { - mgr.maxBlobSize = 16 * 1024 * 1024 - } sf := SupportedFormats.Find(f.ObjectFormat) if sf == nil { return nil, fmt.Errorf("unknown object format: %v", f.ObjectFormat) } - mgr.hashFunc = sf.hashFuncMaker(f.Secret) - mgr.createCipher = sf.createCipher - mgr.keygen = sf.keygen + mgr := &repository{ + storage: r, + format: *f, + } + + mgr.idFormat = *sf for _, o := range options { if err := o(mgr); err != nil { @@ -226,44 +201,35 @@ func NewRepository( } } - mgr.bufferManager = newBufferManager(mgr.maxBlobSize) + mgr.bufferManager = newBufferManager(mgr.format.MaxBlobSize) return mgr, nil } -func splitKeyGenerator(blockIDSize int, keySize int) keygenFunc { - return func(b []byte) ([]byte, []byte) { - if len(b) < blockIDSize+keySize { - panic(fmt.Sprintf("hash result too short: %v, blockIDsize: %v keySize: %v", len(b), blockIDSize, keySize)) - } - blockIDBytes := b[0:blockIDSize] - key := b[blockIDSize : blockIDSize+keySize] - return blockIDBytes, key - } -} - func (mgr *repository) hashBuffer(data []byte) ([]byte, []byte) { - h := mgr.hashFunc() - h.Write(data) - contentHash := h.Sum(nil) - - if mgr.keygen != nil { - return mgr.keygen(contentHash) - } - - return contentHash, nil + return mgr.idFormat.hashBuffer(data, mgr.format.Secret) } -func (mgr *repository) hashBufferForWriting(buffer *bytes.Buffer, prefix string) (ObjectID, io.ReadCloser) { +func (mgr *repository) hashBufferForWriting(buffer *bytes.Buffer, prefix string) (ObjectID, io.ReadCloser, error) { var data []byte if buffer != nil { data = buffer.Bytes() } + var blockCipher cipher.Block + contentHash, cryptoKey := mgr.hashBuffer(data) + if cryptoKey != nil { + var err error + blockCipher, err = mgr.idFormat.createCipher(cryptoKey) + if err != nil { + return "", nil, err + } + } + var objectID ObjectID if len(cryptoKey) > 0 { - objectID = ObjectID(prefix + hex.EncodeToString(contentHash) + ":" + hex.EncodeToString(cryptoKey)) + objectID = ObjectID(prefix + hex.EncodeToString(contentHash) + objectIDEncryptionInfoSeparator + hex.EncodeToString(cryptoKey)) } else { objectID = ObjectID(prefix + hex.EncodeToString(contentHash)) } @@ -272,29 +238,23 @@ func (mgr *repository) hashBufferForWriting(buffer *bytes.Buffer, prefix string) atomic.AddInt64(&mgr.stats.HashedBytes, int64(len(data))) if buffer == nil { - return objectID, ioutil.NopCloser(bytes.NewBuffer(nil)) + return objectID, ioutil.NopCloser(bytes.NewBuffer(nil)), nil } readCloser := mgr.bufferManager.returnBufferOnClose(buffer) readCloser = newCountingReader(readCloser, &mgr.stats.BytesWrittenToStorage) - if mgr.createCipher != nil { - c, err := mgr.createCipher(cryptoKey) - if err != nil { - log.Printf("can't create cipher: %v", err) - panic("can't encrypt block") - } - + if len(cryptoKey) > 0 { // Since we're not sharing the key, all-zero IV is ok. // We don't need to worry about separate MAC either, since hashing content produces object ID. - ctr := cipher.NewCTR(c, constantIV[0:c.BlockSize()]) + ctr := cipher.NewCTR(blockCipher, constantIV[0:blockCipher.BlockSize()]) readCloser = newCountingReader( newEncryptingReader(readCloser, nil, ctr, nil), &mgr.stats.EncryptedBytes) } - return objectID, readCloser + return objectID, readCloser, nil } func (mgr *repository) flattenListChunk( @@ -377,16 +337,12 @@ func (mgr *repository) newRawReader(objectID ObjectID) (io.ReadSeeker, error) { return bytes.NewReader(payload), nil } - if mgr.createCipher == nil { - return nil, errors.New("encrypted object cannot be used with non-encrypted Repository") - } - cryptoKey, err := hex.DecodeString(string(objectID.EncryptionInfo())) if err != nil { return nil, errors.New("malformed encryption key") } - blockCipher, err := mgr.createCipher(cryptoKey) + blockCipher, err := mgr.idFormat.createCipher(cryptoKey) if err != nil { return nil, errors.New("cannot create cipher") } diff --git a/repo/repository_test.go b/repo/repository_test.go index c17fad385..cdcbc5c45 100644 --- a/repo/repository_test.go +++ b/repo/repository_test.go @@ -235,7 +235,7 @@ func TestHMAC(t *testing.T) { content := bytes.Repeat([]byte{0xcd}, 50) s := testFormat() - s.ObjectFormat = "hmac-md5" + s.ObjectFormat = "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 := NewRepository(blob.NewMapStorage(data), s) @@ -390,17 +390,18 @@ func TestFormats(t *testing.T) { oids map[string]ObjectID }{ { - format: makeFormat("md5"), + format: makeFormat("md5"), // MD5-HMAC with secret 'key' oids: map[string]ObjectID{ - "": "Dd41d8cd98f00b204e9800998ecf8427e", - "The quick brown fox jumps over the lazy dog": "D9e107d9d372bb6826bd81d3542a419d6", + "": "D63530468a04e386459855da0063b6596", + "The quick brown fox jumps over the lazy dog": "D80070713463e7749b90c2dc24911e275", }, }, { format: &Format{ Version: "1", - ObjectFormat: "hmac-md5", + ObjectFormat: "md5", MaxBlobSize: 10000, + Secret: []byte{}, // HMAC with zero-byte secret }, oids: map[string]ObjectID{ "": "D74e6f7298a9c2d168935f58c001bad88", @@ -408,50 +409,68 @@ func TestFormats(t *testing.T) { }, }, { - format: makeFormat("hmac-md5"), + format: &Format{ + Version: "1", + ObjectFormat: "md5", + MaxBlobSize: 10000, + Secret: nil, // non-HMAC version + }, + oids: map[string]ObjectID{ + "": "Dd41d8cd98f00b204e9800998ecf8427e", + "The quick brown fox jumps over the lazy dog": "D9e107d9d372bb6826bd81d3542a419d6", + }, + }, + { + format: makeFormat("md5"), oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": "D80070713463e7749b90c2dc24911e275", }, }, { - format: makeFormat("hmac-sha1"), + format: makeFormat("sha1"), oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": "Dde7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", }, }, { - format: makeFormat("hmac-sha256"), + format: makeFormat("sha256"), oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": "Df7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", }, }, { - format: makeFormat("hmac-sha512"), + format: makeFormat("sha512"), oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": "Db42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a", }, }, { - format: makeFormat("hmac-sha256-aes128"), + format: makeFormat("sha256t128-aes128"), oids: map[string]ObjectID{ - "The quick brown fox jumps over the lazy dog": "Df7bc83f430538424b13298e6aa6fb143:ef4d59a14946175997479dbc2d1a3cd8", + "The quick brown fox jumps over the lazy dog": "D4e43650cb2ce7b5a6a2a8d614c13d9b3.f73b9fed1f355b3603e066fb24a39970", }, }, { - format: makeFormat("hmac-sha384-aes256"), + format: makeFormat("sha256-aes128"), oids: map[string]ObjectID{ - "The quick brown fox jumps over the lazy dog": "Dd7f4727e2c0b39ae0f1e40cc96f60242:d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237", + "The quick brown fox jumps over the lazy dog": "D4e43650cb2ce7b5a6a2a8d614c13d9b37c6ee13c4543481074d2c8cf3f597a13.f73b9fed1f355b3603e066fb24a39970", }, }, { - format: makeFormat("hmac-sha512-aes256"), + format: makeFormat("sha384-aes256"), oids: map[string]ObjectID{ - "The quick brown fox jumps over the lazy dog": "Db42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb:82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a", + "The quick brown fox jumps over the lazy dog": "D4e43650cb2ce7b5a6a2a8d614c13d9b37c6ee13c4543481074d2c8cf3f597a136dba4c0e67d5d644049c98ee5369bc0a.f73b9fed1f355b3603e066fb24a39970ac10cbd143babc7f487ef263fe38aeff", + }, + }, + { + format: makeFormat("sha512-aes256"), + oids: map[string]ObjectID{ + "The quick brown fox jumps over the lazy dog": "D4e43650cb2ce7b5a6a2a8d614c13d9b37c6ee13c4543481074d2c8cf3f597a136dba4c0e67d5d644049c98ee5369bc0aaa0e75feaeadd42eb54a9d64f9d0a51d.f73b9fed1f355b3603e066fb24a39970ac10cbd143babc7f487ef263fe38aeff", }, }, } - for _, c := range cases { + for caseIndex, c := range cases { data := map[string][]byte{} st := blob.NewMapStorage(data) @@ -471,7 +490,7 @@ func TestFormats(t *testing.T) { t.Errorf("error: %v", err) } if oid != v { - t.Errorf("invalid oid for %v/%v: %v expected %v", c.format.ObjectFormat, k, oid, v) + t.Errorf("invalid oid for #%v %v/%v: %v expected %v", caseIndex, c.format.ObjectFormat, k, oid, v) } rc, err := repo.Open(oid) @@ -495,7 +514,7 @@ func TestInvalidEncryptionKey(t *testing.T) { st := blob.NewMapStorage(data) format := Format{ Version: "1", - ObjectFormat: "hmac-sha512-aes256", + ObjectFormat: "sha512-aes256", Secret: []byte("key"), MaxBlobSize: 1000, }