From eeacdd1e3dec4ce5ea33eee962fb62e258d060bb Mon Sep 17 00:00:00 2001 From: Jarek Kowalski Date: Sun, 28 Aug 2016 22:16:30 -0700 Subject: [PATCH] Revert "changed JSON encryption key to be base16-encoded instead of base64-encoded" Instead, changed JSON serialization format for ObjectID to be simple string. This reverts commit 4ab146e058c57c7a1fa5f6d48c0264b431a9a150. --- repo/objectid.go | 28 +++++++++++++++++++++++----- repo/objectid_test.go | 27 ++++++++++++++------------- repo/repository.go | 17 ++++------------- repo/repository_test.go | 25 +++++++++++++++++-------- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/repo/objectid.go b/repo/objectid.go index a688020d1..18f38bb34 100644 --- a/repo/objectid.go +++ b/repo/objectid.go @@ -3,6 +3,7 @@ import ( "encoding/base64" "encoding/hex" + "encoding/json" "errors" "strconv" "strings" @@ -40,12 +41,30 @@ // type ObjectID struct { StorageBlock string `json:"block,omitempty"` - EncryptionKey string `json:"encryption,omitempty"` + EncryptionKey []byte `json:"encryption,omitempty"` Indirect int32 `json:"indirect,omitempty"` Content []byte `json:"content,omitempty"` Section *ObjectIDSection `json:"section,omitempty"` } +// MarshalJSON emits ObjectID in standard string format. +func (oid *ObjectID) MarshalJSON() ([]byte, error) { + s := oid.UIString() + return json.Marshal(&s) +} + +// UnmarshalJSON unmarshals Object ID from a JSON string. +func (oid *ObjectID) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *oid, err = ParseObjectID(s) + return err +} + // ObjectIDSection represents details about a section of a repository object. type ObjectIDSection struct { Start int64 `json:"start"` @@ -75,7 +94,7 @@ func (oid *ObjectID) UIString() string { var encryptionSuffix string if len(oid.EncryptionKey) > 0 { - encryptionSuffix = "." + oid.EncryptionKey + encryptionSuffix = "." + hex.EncodeToString(oid.EncryptionKey) } if oid.Indirect > 0 { @@ -239,13 +258,12 @@ func ParseObjectID(s string) (ObjectID, error) { } if firstSeparator > 0 { - enc := content[firstSeparator+1:] - b, err := hex.DecodeString(enc) + 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: enc, + EncryptionKey: b, Indirect: indirectLevel, }, nil } diff --git a/repo/objectid_test.go b/repo/objectid_test.go index dc280e80d..c8f8807a5 100644 --- a/repo/objectid_test.go +++ b/repo/objectid_test.go @@ -1,6 +1,7 @@ package repo import ( + "bytes" "reflect" "strings" "testing" @@ -41,18 +42,18 @@ func TestParseMalformedObjectID(t *testing.T) { func TestParseObjectIDEncryptionInfo(t *testing.T) { cases := []struct { objectID string - expected string + expected []byte }{ - {"B", ""}, - {"BAQIDBA", ""}, - {"Dabcdef", ""}, - {"I1,abcdef", ""}, - {"I2,abcdef", ""}, - {"Dabcdef.00112233", "00112233"}, - {"I1,abcdef.0011223344", "0011223344"}, - {"I2,abcdef.0011223344", "0011223344"}, - {"S1,2,Dabcdef.0011223344", ""}, - {"S1,2,S3,4,Dabcdef.0011223344", ""}, + {"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 { @@ -63,8 +64,8 @@ func TestParseObjectIDEncryptionInfo(t *testing.T) { } actual := objectID.EncryptionKey - if actual != c.expected { - t.Errorf("invalid encryption key for %v: %v, expected: %v", c.objectID, actual, c.expected) + if !bytes.Equal(actual, c.expected) { + t.Errorf("invalid encryption key for %v: %x, expected: %x", c.objectID, actual, c.expected) } uiString := objectID.UIString() diff --git a/repo/repository.go b/repo/repository.go index dc614c418..96bd87621 100644 --- a/repo/repository.go +++ b/repo/repository.go @@ -3,7 +3,6 @@ import ( "bufio" "bytes" - "encoding/hex" "fmt" "io" "log" @@ -228,7 +227,7 @@ func (r *Repository) hashEncryptAndWriteMaybeAsync(buffer *bytes.Buffer, prefix objectID := ObjectID{ StorageBlock: prefix + blockID, - EncryptionKey: hex.EncodeToString(encryptionKey), + EncryptionKey: encryptionKey, } if r.writeBackWorkers > 0 { @@ -276,13 +275,9 @@ func (r *Repository) hashEncryptAndWrite(objectID ObjectID, buffer *bytes.Buffer } // Encryption is requested, encrypt the block in-place. - if len(objectID.EncryptionKey) > 0 { + if objectID.EncryptionKey != nil { atomic.AddInt64(&r.Stats.EncryptedBytes, int64(len(data))) - b, err := hex.DecodeString(objectID.EncryptionKey) - if err != nil { - return NullObjectID, fmt.Errorf("invalid encryption key: %v", err) - } - data, err = r.formatter.Encrypt(data, b) + data, err = r.formatter.Encrypt(data, objectID.EncryptionKey) if err != nil { return NullObjectID, err } @@ -347,11 +342,7 @@ func (r *Repository) newRawReader(objectID ObjectID) (ObjectReader, error) { atomic.AddInt64(&r.Stats.ReadBytes, int64(len(payload))) if len(objectID.EncryptionKey) > 0 { - b, err := hex.DecodeString(objectID.EncryptionKey) - if err != nil { - return nil, fmt.Errorf("invalid encryption key: %v", err) - } - payload, err = r.formatter.Decrypt(payload, b) + payload, err = r.formatter.Decrypt(payload, objectID.EncryptionKey) atomic.AddInt64(&r.Stats.DecryptedBytes, int64(len(payload))) if err != nil { return nil, err diff --git a/repo/repository_test.go b/repo/repository_test.go index ac67df7f2..793d74e0b 100644 --- a/repo/repository_test.go +++ b/repo/repository_test.go @@ -163,9 +163,9 @@ func TestIndirection(t *testing.T) { {dataLength: 250, expectedBlockCount: 3, expectedIndirection: 1}, {dataLength: 1400, expectedBlockCount: 7, expectedIndirection: 3}, {dataLength: 2000, expectedBlockCount: 8, expectedIndirection: 3}, - {dataLength: 3000, expectedBlockCount: 13, expectedIndirection: 4}, - {dataLength: 4000, expectedBlockCount: 15, expectedIndirection: 4}, - {dataLength: 10000, expectedBlockCount: 32, expectedIndirection: 5}, + {dataLength: 3000, expectedBlockCount: 9, expectedIndirection: 3}, + {dataLength: 4000, expectedBlockCount: 14, expectedIndirection: 4}, + {dataLength: 10000, expectedBlockCount: 25, expectedIndirection: 4}, } for _, c := range cases { @@ -438,7 +438,7 @@ func TestFormats(t *testing.T) { oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": ObjectID{ StorageBlock: "d7f4727e2c0b39ae0f1e40cc96f60242", - EncryptionKey: "d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237", + EncryptionKey: mustParseBase16("d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237"), }, }, }, @@ -447,7 +447,7 @@ func TestFormats(t *testing.T) { oids: map[string]ObjectID{ "The quick brown fox jumps over the lazy dog": ObjectID{ StorageBlock: "b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb", - EncryptionKey: "82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a", + EncryptionKey: mustParseBase16("82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"), }, }, }, @@ -533,13 +533,14 @@ func TestInvalidEncryptionKey(t *testing.T) { } // Key too long - rc, err = repo.Open(replaceEncryption(oid, oid.EncryptionKey+"FF")) + 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 := oid.EncryptionKey[2:] + oid.EncryptionKey[len(oid.EncryptionKey)-2:] + 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) @@ -553,7 +554,15 @@ func TestInvalidEncryptionKey(t *testing.T) { } } -func replaceEncryption(oid ObjectID, newEncryption string) ObjectID { +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 +}