mirror of
https://github.com/kopia/kopia.git
synced 2026-03-15 04:38:56 -04:00
tweaked object ID format
This commit is contained in:
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
153
repo/format.go
153
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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user