github.com/creachadair/ffs@v0.17.3/storage/codecs/encrypted/encrypted_test.go (about) 1 // Copyright 2019 Michael J. Fromberger. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package encrypted_test 16 17 import ( 18 "bytes" 19 "crypto/aes" 20 "crypto/cipher" 21 "encoding/binary" 22 "testing" 23 24 "github.com/creachadair/ffs/storage/codecs/encrypted" 25 "github.com/creachadair/ffs/storage/encoded" 26 ) 27 28 var _ encoded.Codec = (*encrypted.Codec)(nil) 29 30 // A fake Keyring implementation with static keys. 31 type keyring []string 32 33 func (k *keyring) Has(id int) bool { return id > 0 && id-1 < len(*k) } 34 35 func (k *keyring) Get(id int, buf []byte) []byte { return append(buf, (*k)[id-1]...) } 36 37 func (k *keyring) GetActive(buf []byte) (int, []byte) { 38 return len(*k), append(buf, (*k)[len(*k)-1]...) 39 } 40 41 func (k *keyring) addKey(s string) { *k = append(*k, s) } 42 43 func TestCodec(t *testing.T) { 44 newCipher := func(key []byte) (cipher.AEAD, error) { 45 blk, err := aes.NewCipher(key) 46 if err != nil { 47 return nil, err 48 } 49 return cipher.NewGCM(blk) 50 } 51 kr := &keyring{"00000000000000000000000000000000"} 52 e := encrypted.New(newCipher, kr) 53 54 checkEncrypt := func(ptext string) []byte { 55 t.Helper() 56 var buf bytes.Buffer 57 if err := e.Encode(&buf, []byte(ptext)); err != nil { 58 t.Fatalf("Encode failed: %v", err) 59 } 60 return buf.Bytes() 61 } 62 checkDecrypt := func(ctext []byte, want string) { 63 t.Helper() 64 var buf bytes.Buffer 65 if err := e.Decode(&buf, ctext); err != nil { 66 t.Fatalf("Decode failed: %v", err) 67 } 68 if buf.String() != want { 69 t.Errorf("Decode:\ngot: %s\nwant: %s", buf.String(), want) 70 } 71 } 72 73 const value = "some of what a fool thinks often remains" 74 t.Logf("Input (%d bytes): %q", len(value), value) 75 76 // Encode the test value through the encrypted codec. 77 // Log the stored block for debugging purposes. 78 enc := checkEncrypt(value) 79 t.Logf("Stored block 1 (%d bytes):\n%#q", len(enc), enc) 80 if id := int(binary.BigEndian.Uint32(enc[1:5])); id != 1 { 81 t.Errorf("Key ID is %d, want 1", id) 82 } 83 84 checkDecrypt(enc, value) 85 86 // Add a new key and verify that we can still decrypt the old block. 87 kr.addKey("11111111111111111111111111111111") 88 checkDecrypt(enc, value) 89 90 // Encrypt a new block, which should use the new key. 91 enc2 := checkEncrypt(value) 92 t.Logf("Stored block 2 (%d bytes):\n%#q", len(enc2), enc2) 93 if id := int(binary.BigEndian.Uint32(enc2[1:5])); id != 2 { 94 t.Errorf("Key ID is %d, want 2", id) 95 } 96 97 checkDecrypt(enc, value) 98 checkDecrypt(enc2, value) 99 100 // Verify that we can also handle the legacy block format without a key ID. 101 // This format always uses the first key version in the ring. 102 legacy := make([]byte, 0, len(enc)-4) 103 legacy = append(legacy, enc[0]&0x7f) 104 legacy = append(legacy, enc[5:]...) 105 t.Logf("Legacy block (%d bytes):\n%#q", len(legacy), legacy) 106 107 checkDecrypt(legacy, value) 108 }