github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/cmdccl/enc_utils/main.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package main 10 11 import ( 12 "context" 13 "crypto/aes" 14 "encoding/binary" 15 "encoding/hex" 16 "flag" 17 "fmt" 18 "io/ioutil" 19 "path/filepath" 20 21 "github.com/cockroachdb/cockroach/pkg/ccl/storageccl/engineccl/enginepbccl" 22 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 23 "github.com/cockroachdb/cockroach/pkg/util/log" 24 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 25 "github.com/cockroachdb/errors" 26 ) 27 28 const fileRegistryPath = "COCKROACHDB_REGISTRY" 29 const keyRegistryPath = "COCKROACHDB_DATA_KEYS" 30 const currentPath = "CURRENT" 31 const optionsPathGlob = "OPTIONS-*" 32 33 var dbDir = flag.String("db-dir", "", "path to the db directory") 34 var storeKeyPath = flag.String("store-key", "", "path to the active store key") 35 36 type fileEntry struct { 37 envType enginepb.EnvType 38 settings enginepbccl.EncryptionSettings 39 } 40 41 func (f fileEntry) String() string { 42 ret := fmt.Sprintf(" env type: %d, %s\n", 43 f.envType, f.settings.EncryptionType) 44 if f.settings.EncryptionType != enginepbccl.EncryptionType_Plaintext { 45 ret += fmt.Sprintf(" keyID: %s\n nonce: % x\n counter: %d\n", 46 f.settings.KeyId, 47 f.settings.Nonce, 48 f.settings.Counter) 49 } 50 return ret 51 } 52 53 type keyEntry struct { 54 encryptionType enginepbccl.EncryptionType 55 rawKey []byte 56 } 57 58 func (k keyEntry) String() string { 59 return fmt.Sprintf("%s len: %d", k.encryptionType, len(k.rawKey)) 60 } 61 62 var fileRegistry = map[string]fileEntry{} 63 var keyRegistry = map[string]keyEntry{} 64 65 func loadFileRegistry() { 66 data, err := ioutil.ReadFile(filepath.Join(*dbDir, fileRegistryPath)) 67 if err != nil { 68 log.Fatalf(context.Background(), "could not read %s: %v", fileRegistryPath, err) 69 } 70 71 var reg enginepb.FileRegistry 72 if err := protoutil.Unmarshal(data, ®); err != nil { 73 log.Fatalf(context.Background(), "could not unmarshal %s: %v", fileRegistryPath, err) 74 } 75 76 log.Infof(context.Background(), "file registry version: %s", reg.Version) 77 log.Infof(context.Background(), "file registry contains %d entries", len(reg.Files)) 78 for name, entry := range reg.Files { 79 var encSettings enginepbccl.EncryptionSettings 80 settings := entry.EncryptionSettings 81 if err := protoutil.Unmarshal(settings, &encSettings); err != nil { 82 log.Fatalf(context.Background(), "could not unmarshal encryption setting for %s: %v", name, err) 83 } 84 85 fileRegistry[name] = fileEntry{entry.EnvType, encSettings} 86 87 log.Infof(context.Background(), " %-30s level: %-8s type: %-12s keyID: %s", name, entry.EnvType, encSettings.EncryptionType, encSettings.KeyId[:8]) 88 } 89 } 90 91 func loadStoreKey() { 92 if len(*storeKeyPath) == 0 || *storeKeyPath == "plain" { 93 log.Infof(context.Background(), "No store key specified") 94 return 95 } 96 97 data, err := ioutil.ReadFile(*storeKeyPath) 98 if err != nil { 99 log.Fatalf(context.Background(), "could not read %s: %v", *storeKeyPath, err) 100 } 101 102 var k keyEntry 103 switch len(data) { 104 case 48: 105 k.encryptionType = enginepbccl.EncryptionType_AES128_CTR 106 case 56: 107 k.encryptionType = enginepbccl.EncryptionType_AES192_CTR 108 case 64: 109 k.encryptionType = enginepbccl.EncryptionType_AES256_CTR 110 default: 111 log.Fatalf(context.Background(), "wrong key length %d, want 32 bytes + AES length", len(data)) 112 } 113 114 // Hexadecimal representation of the first 32 bytes. 115 id := hex.EncodeToString(data[0:32]) 116 // Raw key is the rest. 117 k.rawKey = data[32:] 118 119 keyRegistry[id] = k 120 121 log.Infof(context.Background(), "store key: %s", k) 122 } 123 124 func loadKeyRegistry() { 125 data, err := readFile(keyRegistryPath) 126 if err != nil { 127 log.Fatalf(context.Background(), "could not read %s: %v", keyRegistryPath, err) 128 } 129 130 var reg enginepbccl.DataKeysRegistry 131 if err := protoutil.Unmarshal(data, ®); err != nil { 132 log.Fatalf(context.Background(), "could not unmarshal %s: %v", keyRegistryPath, err) 133 } 134 135 log.Infof(context.Background(), "key registry contains %d store keys(s) and %d data key(s)", 136 len(reg.StoreKeys), len(reg.DataKeys)) 137 for _, e := range reg.StoreKeys { 138 log.Infof(context.Background(), " store key: type: %-12s %v", e.EncryptionType, e) 139 } 140 for _, e := range reg.DataKeys { 141 log.Infof(context.Background(), " data key: type: %-12s %v", e.Info.EncryptionType, e.Info) 142 } 143 for k, e := range reg.DataKeys { 144 keyRegistry[k] = keyEntry{e.Info.EncryptionType, e.Key} 145 } 146 } 147 148 func loadCurrent() { 149 data, err := readFile(currentPath) 150 if err != nil { 151 log.Fatalf(context.Background(), "could not read %s: %v", currentPath, err) 152 } 153 154 log.Infof(context.Background(), "current: %s", string(data)) 155 } 156 157 func loadOptions() { 158 absGlob := filepath.Join(*dbDir, optionsPathGlob) 159 paths, err := filepath.Glob(absGlob) 160 if err != nil { 161 log.Fatalf(context.Background(), "problem finding files matching %s: %v", absGlob, err) 162 } 163 164 for _, f := range paths { 165 fname := filepath.Base(f) 166 data, err := readFile(fname) 167 if err != nil { 168 log.Fatalf(context.Background(), "could not read %s: %v", fname, err) 169 } 170 log.Infof(context.Background(), "options file: %s starts with: %s", fname, string(data[:100])) 171 } 172 } 173 174 func readFile(filename string) ([]byte, error) { 175 if len(filename) == 0 { 176 return nil, errors.Errorf("filename is empty") 177 } 178 179 absPath := filename 180 if filename[0] != '/' { 181 absPath = filepath.Join(*dbDir, filename) 182 } 183 184 data, err := ioutil.ReadFile(absPath) 185 if err != nil { 186 return nil, errors.Errorf("could not read %s: %v", absPath, err) 187 } 188 189 reg, ok := fileRegistry[filename] 190 if !ok || reg.settings.EncryptionType == enginepbccl.EncryptionType_Plaintext { 191 // Plaintext: do nothing. 192 log.Infof(context.Background(), "reading plaintext %s", absPath) 193 return data, nil 194 } 195 196 // Encrypted: find the key. 197 key, ok := keyRegistry[reg.settings.KeyId] 198 if !ok { 199 return nil, errors.Errorf("could not find key %s for file %s", reg.settings.KeyId, absPath) 200 } 201 log.Infof(context.Background(), "decrypting %s with %s key %s...", filename, reg.settings.EncryptionType, reg.settings.KeyId[:8]) 202 203 cipher, err := aes.NewCipher(key.rawKey) 204 if err != nil { 205 return nil, errors.Errorf("could not build AES cipher for file %s: %v", absPath, err) 206 } 207 208 size := len(data) 209 counter := reg.settings.Counter 210 nonce := reg.settings.Nonce 211 if len(nonce) != 12 { 212 log.Fatalf(context.Background(), "nonce has wrong length: %d, expected 12", len(nonce)) 213 } 214 215 iv := make([]byte, aes.BlockSize) 216 for offset := 0; offset < size; offset += aes.BlockSize { 217 // Put nonce at beginning of IV. 218 copy(iv, nonce) 219 // Write counter to end of IV in network byte order. 220 binary.BigEndian.PutUint32(iv[12:], counter) 221 // Increment counter for next block. 222 counter++ 223 224 // Encrypt IV (AES CTR mode is always encrypt). 225 cipher.Encrypt(iv, iv) 226 227 // XOR data with decrypted IV. We may have a partial block at the end of 'data'. 228 for i := 0; i < aes.BlockSize; i++ { 229 pos := offset + i 230 if pos >= size { 231 // Partial block. 232 break 233 } 234 data[pos] = data[pos] ^ iv[i] 235 } 236 } 237 238 return data, nil 239 } 240 241 func main() { 242 flag.Parse() 243 loadStoreKey() 244 loadFileRegistry() 245 loadKeyRegistry() 246 loadCurrent() 247 loadOptions() 248 }