github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/trust/key_load_test.go (about) 1 package trust 2 3 import ( 4 "encoding/pem" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 "testing" 11 12 "github.com/docker/cli/cli/config" 13 "github.com/docker/cli/internal/test" 14 "github.com/theupdateframework/notary" 15 "github.com/theupdateframework/notary/passphrase" 16 "github.com/theupdateframework/notary/storage" 17 "github.com/theupdateframework/notary/trustmanager" 18 tufutils "github.com/theupdateframework/notary/tuf/utils" 19 "gotest.tools/assert" 20 is "gotest.tools/assert/cmp" 21 "gotest.tools/skip" 22 ) 23 24 func TestTrustKeyLoadErrors(t *testing.T) { 25 noSuchFile := "stat iamnotakey: no such file or directory" 26 if runtime.GOOS == "windows" { 27 noSuchFile = "CreateFile iamnotakey: The system cannot find the file specified." 28 } 29 testCases := []struct { 30 name string 31 args []string 32 expectedError string 33 expectedOutput string 34 }{ 35 { 36 name: "not-enough-args", 37 expectedError: "exactly 1 argument", 38 expectedOutput: "", 39 }, 40 { 41 name: "too-many-args", 42 args: []string{"iamnotakey", "alsonotakey"}, 43 expectedError: "exactly 1 argument", 44 expectedOutput: "", 45 }, 46 { 47 name: "not-a-key", 48 args: []string{"iamnotakey"}, 49 expectedError: "refusing to load key from iamnotakey: " + noSuchFile, 50 expectedOutput: "Loading key from \"iamnotakey\"...\n", 51 }, 52 { 53 name: "bad-key-name", 54 args: []string{"iamnotakey", "--name", "KEYNAME"}, 55 expectedError: "key name \"KEYNAME\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", 56 expectedOutput: "", 57 }, 58 } 59 tmpDir, err := ioutil.TempDir("", "docker-key-load-test-") 60 assert.NilError(t, err) 61 defer os.RemoveAll(tmpDir) 62 config.SetDir(tmpDir) 63 64 for _, tc := range testCases { 65 cli := test.NewFakeCli(&fakeClient{}) 66 cmd := newKeyLoadCommand(cli) 67 cmd.SetArgs(tc.args) 68 cmd.SetOutput(ioutil.Discard) 69 assert.ErrorContains(t, cmd.Execute(), tc.expectedError) 70 assert.Check(t, is.Contains(cli.OutBuffer().String(), tc.expectedOutput)) 71 } 72 } 73 74 var rsaPrivKeyFixture = []byte(`-----BEGIN RSA PRIVATE KEY----- 75 MIIEpAIBAAKCAQEAs7yVMzCw8CBZPoN+QLdx3ZzbVaHnouHIKu+ynX60IZ3stpbb 76 6rowu78OWON252JcYJqe++2GmdIgbBhg+mZDwhX0ZibMVztJaZFsYL+Ch/2J9KqD 77 A5NtE1s/XdhYoX5hsv7W4ok9jLFXRYIMj+T4exJRlR4f4GP9p0fcqPWd9/enPnlJ 78 JFTmu0DXJTZUMVS1UrXUy5t/DPXdrwyl8pM7VCqO3bqK7jqE6mWawdTkEeiku1fJ 79 ydP0285uiYTbj1Q38VVhPwXzMuLbkaUgRJhCI4BcjfQIjtJLbWpS+VdhUEvtgMVx 80 XJMKxCVGG69qjXyj9TjI7pxanb/bWglhovJN9wIDAQABAoIBAQCSnMsLxbUfOxPx 81 RWuwOLN+NZxIvtfnastQEtSdWiRvo5Xa3zYmw5hLHa8DXRC57+cwug/jqr54LQpb 82 gotg1hiBck05In7ezTK2FXTVeoJskal91bUnLpP0DSOkVnz9xszFKNF6Wr7FTEfH 83 IC1FF16Fbcz0mW0hKg9X6+uYOzqPcKpQRwli5LAwhT18Alf9h4/3NCeKotiJyr2J 84 xvcEH1eY2m2c/jQZurBkys7qBC3+i8LJEOW8MBQt7mxajwfbU91wtP2YoqMcoYiS 85 zsPbYp7Ui2t4G9Yn+OJw+uj4RGP1Bo4nSyRxWDtg+8Zug/JYU6/s+8kVRpiGffd3 86 T1GvoxUhAoGBAOnPDWG/g1xlJf65Rh71CxMs638zhYbIloU2K4Rqr05DHe7GryTS 87 9hLVrwhHddK+KwfVbR8HFMPo1DC/NVbuKt8StTAadAu3HsC088gWd28nOiGAWuvH 88 Bo3x/DYQGYwGFfoo4rzCOgMj6DJjXmcWEXNv3NDMoXoYpkxa0g6zZDyHAoGBAMTL 89 t7EUneJT+Mm7wyL1I5bmaT/HFwqoUQB2ccBPVD8p1el62NgLdfhOa8iNlBVhMrlh 90 2aTjrMlSPcjr9sCgKrLcenSWw+2qFsf4+SmV01ntB9kWes2phXpnB0ynXIcbeG05 91 +BLxbqDTVV0Iqh4r/dGeplyV2WyL3mTpkT3hRq8RAoGAZ93degEUICWnHWO9LN97 92 Dge0joua0+ekRoVsC6VBP6k9UOfewqMdQfy/hxQH2Zk1kINVuKTyqp1yNj2bOoUP 93 co3jA/2cc9/jv4QjkE26vRxWDK/ytC90T/aiLno0fyns9XbYUzaNgvuemVPfijgZ 94 hIi7Nd7SFWWB6wWlr3YuH10CgYEAwh7JVa2mh8iZEjVaKTNyJbmmfDjgq6yYKkKr 95 ti0KRzv3O9Xn7ERx27tPaobtWaGFLYQt8g57NCMhuv23aw8Sz1fYmwTUw60Rx7P5 96 42FdF8lOAn/AJvpfJfxXIO+9v7ADPIr//3+TxqRwAdM4K4btWkaKh61wyTe26gfT 97 MxzyYmECgYAnlU5zsGyiZqwoXVktkhtZrE7Qu0SoztzFb8KpvFNmMTPF1kAAYmJY 98 GIhbizeGJ3h4cUdozKmt8ZWIt6uFDEYCqEA7XF4RH75dW25x86mpIPO7iRl9eisY 99 IsLeMYqTIwXAwGx6Ka9v5LOL1kzcHQ2iVj6+QX+yoptSft1dYa9jOA== 100 -----END RSA PRIVATE KEY-----`) 101 102 const rsaPrivKeyID = "ee69e8e07a14756ad5ff0aca2336b37f86b0ac1710d1f3e94440081e080aecd7" 103 104 var ecPrivKeyFixture = []byte(`-----BEGIN EC PRIVATE KEY----- 105 MHcCAQEEINfxKtDH3ug7ZIQPDyeAzujCdhw36D+bf9ToPE1A7YEyoAoGCCqGSM49 106 AwEHoUQDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4cH3nzy2O6Q/ct4BjOBKa+WCdR 107 tPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ== 108 -----END EC PRIVATE KEY-----`) 109 110 const ecPrivKeyID = "46157cb0becf9c72c3219e11d4692424fef9bf4460812ccc8a71a3dfcafc7e60" 111 112 var testKeys = map[string][]byte{ 113 ecPrivKeyID: ecPrivKeyFixture, 114 rsaPrivKeyID: rsaPrivKeyFixture, 115 } 116 117 func TestLoadKeyFromPath(t *testing.T) { 118 skip.If(t, runtime.GOOS == "windows") 119 for keyID, keyBytes := range testKeys { 120 t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) { 121 testLoadKeyFromPath(t, keyID, keyBytes) 122 }) 123 } 124 } 125 126 func testLoadKeyFromPath(t *testing.T, privKeyID string, privKeyFixture []byte) { 127 privKeyDir, err := ioutil.TempDir("", "key-load-test-") 128 assert.NilError(t, err) 129 defer os.RemoveAll(privKeyDir) 130 privKeyFilepath := filepath.Join(privKeyDir, "privkey.pem") 131 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, notary.PrivNoExecPerms)) 132 133 keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") 134 assert.NilError(t, err) 135 defer os.RemoveAll(keyStorageDir) 136 137 passwd := "password" 138 cannedPasswordRetriever := passphrase.ConstantRetriever(passwd) 139 keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) 140 assert.NilError(t, err) 141 privKeyImporters := []trustmanager.Importer{keyFileStore} 142 143 // get the privKeyBytes 144 privKeyBytes, err := getPrivKeyBytesFromPath(privKeyFilepath) 145 assert.NilError(t, err) 146 147 // import the key to our keyStorageDir 148 assert.Check(t, loadPrivKeyBytesToStore(privKeyBytes, privKeyImporters, privKeyFilepath, "signer-name", cannedPasswordRetriever)) 149 150 // check that the appropriate ~/<trust_dir>/private/<key_id>.key file exists 151 expectedImportKeyPath := filepath.Join(keyStorageDir, notary.PrivDir, privKeyID+"."+notary.KeyExtension) 152 _, err = os.Stat(expectedImportKeyPath) 153 assert.NilError(t, err) 154 155 // verify the key content 156 from, _ := os.OpenFile(expectedImportKeyPath, os.O_RDONLY, notary.PrivExecPerms) 157 defer from.Close() 158 fromBytes, _ := ioutil.ReadAll(from) 159 keyPEM, _ := pem.Decode(fromBytes) 160 assert.Check(t, is.Equal("signer-name", keyPEM.Headers["role"])) 161 // the default GUN is empty 162 assert.Check(t, is.Equal("", keyPEM.Headers["gun"])) 163 // assert encrypted header 164 assert.Check(t, is.Equal("ENCRYPTED PRIVATE KEY", keyPEM.Type)) 165 166 decryptedKey, err := tufutils.ParsePKCS8ToTufKey(keyPEM.Bytes, []byte(passwd)) 167 assert.NilError(t, err) 168 fixturePEM, _ := pem.Decode(privKeyFixture) 169 assert.Check(t, is.DeepEqual(fixturePEM.Bytes, decryptedKey.Private())) 170 } 171 172 func TestLoadKeyTooPermissive(t *testing.T) { 173 skip.If(t, runtime.GOOS == "windows") 174 for keyID, keyBytes := range testKeys { 175 t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) { 176 testLoadKeyTooPermissive(t, keyBytes) 177 }) 178 } 179 } 180 181 func testLoadKeyTooPermissive(t *testing.T, privKeyFixture []byte) { 182 privKeyDir, err := ioutil.TempDir("", "key-load-test-") 183 assert.NilError(t, err) 184 defer os.RemoveAll(privKeyDir) 185 privKeyFilepath := filepath.Join(privKeyDir, "privkey477.pem") 186 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0477)) 187 188 keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") 189 assert.NilError(t, err) 190 defer os.RemoveAll(keyStorageDir) 191 192 // import the key to our keyStorageDir 193 _, err = getPrivKeyBytesFromPath(privKeyFilepath) 194 expected := fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) 195 assert.Error(t, err, expected) 196 197 privKeyFilepath = filepath.Join(privKeyDir, "privkey667.pem") 198 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0677)) 199 200 _, err = getPrivKeyBytesFromPath(privKeyFilepath) 201 expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) 202 assert.Error(t, err, expected) 203 204 privKeyFilepath = filepath.Join(privKeyDir, "privkey777.pem") 205 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0777)) 206 207 _, err = getPrivKeyBytesFromPath(privKeyFilepath) 208 expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) 209 assert.Error(t, err, expected) 210 211 privKeyFilepath = filepath.Join(privKeyDir, "privkey400.pem") 212 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0400)) 213 214 _, err = getPrivKeyBytesFromPath(privKeyFilepath) 215 assert.NilError(t, err) 216 217 privKeyFilepath = filepath.Join(privKeyDir, "privkey600.pem") 218 assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0600)) 219 220 _, err = getPrivKeyBytesFromPath(privKeyFilepath) 221 assert.NilError(t, err) 222 } 223 224 var pubKeyFixture = []byte(`-----BEGIN PUBLIC KEY----- 225 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4c 226 H3nzy2O6Q/ct4BjOBKa+WCdRtPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ== 227 -----END PUBLIC KEY-----`) 228 229 func TestLoadPubKeyFailure(t *testing.T) { 230 skip.If(t, runtime.GOOS == "windows") 231 pubKeyDir, err := ioutil.TempDir("", "key-load-test-pubkey-") 232 assert.NilError(t, err) 233 defer os.RemoveAll(pubKeyDir) 234 pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") 235 assert.NilError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) 236 keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") 237 assert.NilError(t, err) 238 defer os.RemoveAll(keyStorageDir) 239 240 passwd := "password" 241 cannedPasswordRetriever := passphrase.ConstantRetriever(passwd) 242 keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) 243 assert.NilError(t, err) 244 privKeyImporters := []trustmanager.Importer{keyFileStore} 245 246 pubKeyBytes, err := getPrivKeyBytesFromPath(pubKeyFilepath) 247 assert.NilError(t, err) 248 249 // import the key to our keyStorageDir - it should fail 250 err = loadPrivKeyBytesToStore(pubKeyBytes, privKeyImporters, pubKeyFilepath, "signer-name", cannedPasswordRetriever) 251 expected := fmt.Sprintf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", pubKeyFilepath) 252 assert.Error(t, err, expected) 253 }