diff --git a/repo/format.go b/repo/format.go index 545ba53bc..2e516f027 100644 --- a/repo/format.go +++ b/repo/format.go @@ -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") diff --git a/repo/objectid.go b/repo/objectid.go index 5615dc0c4..8a3c91e36 100644 --- a/repo/objectid.go +++ b/repo/objectid.go @@ -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 } } diff --git a/repo/objectid_test.go b/repo/objectid_test.go index ac8a7beef..a5ecf9998 100644 --- a/repo/objectid_test.go +++ b/repo/objectid_test.go @@ -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) - } - } -} diff --git a/repo/repository_test.go b/repo/repository_test.go index 85a3efd55..9c939768d 100644 --- a/repo/repository_test.go +++ b/repo/repository_test.go @@ -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 -}