1%% %CopyrightBegin% 2%% 3%% Copyright Ericsson AB 2005-2020. All Rights Reserved. 4%% 5%% Licensed under the Apache License, Version 2.0 (the "License"); 6%% you may not use this file except in compliance with the License. 7%% You may obtain a copy of the License at 8%% 9%% http://www.apache.org/licenses/LICENSE-2.0 10%% 11%% Unless required by applicable law or agreed to in writing, software 12%% distributed under the License is distributed on an "AS IS" BASIS, 13%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14%% See the License for the specific language governing permissions and 15%% limitations under the License. 16%% 17%% %CopyrightEnd% 18%% 19 20%% 21-module(ssh_pubkey_SUITE). 22 23%% Note: This directive should only be used in test suites. 24-export([ 25 suite/0, 26 all/0, 27 groups/0, 28 init_per_suite/1, 29 end_per_suite/1, 30 init_per_group/2, 31 end_per_group/2, 32 init_per_testcase/2, 33 end_per_testcase/2 34 ]). 35 36-export([ 37 check_dsa_disabled/1, 38 check_rsa_sha1_disabled/1, 39 connect_dsa_to_dsa/1, 40 connect_dsa_to_ecdsa/1, 41 connect_dsa_to_ed25519/1, 42 connect_dsa_to_ed448/1, 43 connect_dsa_to_rsa_sha2/1, 44 connect_ecdsa_to_dsa/1, 45 connect_ecdsa_to_ecdsa/1, 46 connect_ecdsa_to_ed25519/1, 47 connect_ecdsa_to_ed448/1, 48 connect_ecdsa_to_rsa_sha2/1, 49 connect_ed25519_to_dsa/1, 50 connect_ed25519_to_ecdsa/1, 51 connect_ed25519_to_ed25519/1, 52 connect_ed25519_to_ed448/1, 53 connect_ed25519_to_rsa_sha2/1, 54 connect_ed448_to_dsa/1, 55 connect_ed448_to_ecdsa/1, 56 connect_ed448_to_ed25519/1, 57 connect_ed448_to_ed448/1, 58 connect_ed448_to_rsa_sha2/1, 59 connect_rsa_sha1_to_dsa/1, 60 connect_rsa_sha2_to_dsa/1, 61 connect_rsa_sha2_to_ecdsa/1, 62 connect_rsa_sha2_to_ed25519/1, 63 connect_rsa_sha2_to_ed448/1, 64 connect_rsa_sha2_to_rsa_sha2/1, 65 66 ssh_rsa_public_key/1, 67 ssh_dsa_public_key/1, 68 ssh_ecdsa_public_key/1, 69 ssh_rfc4716_rsa_comment/1, 70 ssh_rfc4716_dsa_comment/1, 71 ssh_rfc4716_rsa_subject/1, 72 ssh_list_public_key/1, 73 ssh_known_hosts/1, 74 ssh1_known_hosts/1, 75 ssh_auth_keys/1, 76 ssh1_auth_keys/1, 77 ssh_openssh_key_with_comment/1, 78 ssh_openssh_key_long_header/1, 79 80 ssh_hostkey_fingerprint_md5_implicit/1, 81 ssh_hostkey_fingerprint_md5/1, 82 ssh_hostkey_fingerprint_sha/1, 83 ssh_hostkey_fingerprint_sha256/1, 84 ssh_hostkey_fingerprint_sha384/1, 85 ssh_hostkey_fingerprint_sha512/1, 86 ssh_hostkey_fingerprint_list/1, 87 88 chk_known_hosts/1 89 ]). 90 91-include_lib("common_test/include/ct.hrl"). 92-include_lib("public_key/include/public_key.hrl"). 93-include("ssh_test_lib.hrl"). 94 95%%%---------------------------------------------------------------- 96%%% Common Test interface functions ------------------------------- 97%%%---------------------------------------------------------------- 98 99suite() -> 100 [{ct_hooks,[ts_install_cth]}, 101 {timetrap,{seconds,40}}]. 102 103all() -> 104 [{group, old_format}, 105 {group, new_format}, 106 {group, option_space}, 107 {group, ssh_hostkey_fingerprint}, 108 {group, ssh_public_key_decode_encode}, 109 chk_known_hosts 110 ]. 111 112 113-define(tests_old, [connect_rsa_sha2_to_rsa_sha2, 114 connect_rsa_sha1_to_dsa, 115 connect_rsa_sha2_to_dsa, 116 connect_rsa_sha2_to_ecdsa, 117 connect_dsa_to_rsa_sha2, 118 connect_dsa_to_dsa, 119 connect_dsa_to_ecdsa, 120 connect_ecdsa_to_rsa_sha2, 121 connect_ecdsa_to_dsa, 122 connect_ecdsa_to_ecdsa 123 ]). 124 125-define(tests_new, [connect_dsa_to_ed25519, 126 connect_dsa_to_ed448, 127 connect_ecdsa_to_ed25519, 128 connect_ecdsa_to_ed448, 129 connect_ed25519_to_dsa, 130 connect_ed25519_to_ecdsa, 131 connect_ed25519_to_ed448, 132 connect_ed25519_to_ed25519, 133 connect_ed25519_to_rsa_sha2, 134 connect_ed448_to_dsa, 135 connect_ed448_to_ecdsa, 136 connect_ed448_to_ed25519, 137 connect_ed448_to_ed448, 138 connect_ed448_to_rsa_sha2, 139 connect_rsa_sha2_to_ed25519, 140 connect_rsa_sha2_to_ed448 141 | ?tests_old % but taken from the new format directory 142 ]). 143 144groups() -> 145 [{new_format, [], ?tests_new}, 146 {old_format, [], [check_dsa_disabled, check_rsa_sha1_disabled | ?tests_old++[{group,passphrase}] ]}, 147 {passphrase, [], ?tests_old}, 148 {option_space,[], [{group,new_format}]}, 149 150 {ssh_hostkey_fingerprint, [], 151 [ssh_hostkey_fingerprint_md5_implicit, 152 ssh_hostkey_fingerprint_md5, 153 ssh_hostkey_fingerprint_sha, 154 ssh_hostkey_fingerprint_sha256, 155 ssh_hostkey_fingerprint_sha384, 156 ssh_hostkey_fingerprint_sha512, 157 ssh_hostkey_fingerprint_list]}, 158 159 {ssh_public_key_decode_encode, [], 160 [ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key, 161 ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment, 162 ssh_rfc4716_rsa_subject, 163 ssh_list_public_key, 164 ssh_known_hosts, %% ssh1_known_hosts, 165 ssh_auth_keys, %% ssh1_auth_keys, 166 ssh_openssh_key_with_comment, 167 ssh_openssh_key_long_header]} 168 ]. 169 170 171%%%---------------------------------------------------------------- 172init_per_suite(Config) -> 173 ?CHECK_CRYPTO( 174 begin 175 ssh:start(), 176 [{client_opts,[]}, 177 {daemon_opts,[]} 178 | Config] 179 end). 180 181end_per_suite(_onfig) -> 182 ssh:stop(). 183 184%%%---------------------------------------------------------------- 185init_per_group(new_format, Config) -> 186 Dir = filename:join(proplists:get_value(data_dir,Config), "new_format"), 187 [{fmt,new_format}, 188 {key_src_dir,Dir} | Config]; 189 190init_per_group(old_format, Config) -> 191 Dir = filename:join(proplists:get_value(data_dir,Config), "old_format"), 192 [{fmt,old_format}, 193 {key_src_dir,Dir} | Config]; 194 195init_per_group(option_space, Config) -> 196 extend_optsL([client_opts,daemon_opts], 197 [{key_cb, {ssh_file, [{optimize, space}]}}], 198 Config); 199 200init_per_group(passphrase, Config0) -> 201 case supported(hashs, md5) of 202 true -> 203 Dir = filename:join(proplists:get_value(data_dir,Config0), "old_format_passphrase"), 204 PassPhrases = [{K,"somepwd"} || K <- [dsa_pass_phrase, 205 rsa_pass_phrase, 206 ecdsa_pass_phrase]], 207 Config1 = extend_optsL(client_opts, PassPhrases, Config0), 208 replace_opt(key_src_dir, Dir, Config1); 209 false -> 210 {skip, "Unsupported hash"} 211 end; 212 213init_per_group(ssh_public_key_decode_encode, Config) -> 214 [{pk_data_dir, 215 filename:join([proplists:get_value(data_dir, Config), 216 "public_key"]) 217 } | Config]; 218 219init_per_group(_, Config) -> 220 Config. 221 222 223extend_optsL(OptNames, Values, Config) when is_list(OptNames) -> 224 lists:foldl(fun(N, Cnf) -> 225 extend_optsL(N, Values, Cnf) 226 end, Config, OptNames); 227extend_optsL(OptName, Values, Config) when is_atom(OptName) -> 228 Opts = proplists:get_value(OptName, Config), 229 replace_opt(OptName, Values ++ Opts, Config). 230 231replace_opt(OptName, Value, Config) -> 232 lists:keyreplace(OptName, 1, Config, {OptName,Value}). 233 234 235 236end_per_group(_, Config) -> 237 Config. 238 239%%%---------------------------------------------------------------- 240init_per_testcase(connect_rsa_sha2_to_rsa_sha2, Config0) -> 241 setup_user_system_dir(rsa_sha2, rsa_sha2, Config0); 242init_per_testcase(connect_rsa_sha1_to_dsa, Config0) -> 243 setup_user_system_dir(rsa_sha1, dsa, Config0); 244init_per_testcase(connect_rsa_sha2_to_dsa, Config0) -> 245 setup_user_system_dir(rsa_sha2, dsa, Config0); 246init_per_testcase(connect_rsa_sha2_to_ecdsa, Config0) -> 247 setup_user_system_dir(rsa_sha2, ecdsa, Config0); 248init_per_testcase(connect_rsa_sha2_to_ed25519, Config0) -> 249 setup_user_system_dir(rsa_sha2, ed25519, Config0); 250init_per_testcase(connect_rsa_sha2_to_ed448, Config0) -> 251 setup_user_system_dir(rsa_sha2, ed448, Config0); 252init_per_testcase(connect_dsa_to_rsa_sha2, Config0) -> 253 setup_user_system_dir(dsa, rsa_sha2, Config0); 254init_per_testcase(connect_dsa_to_dsa, Config0) -> 255 setup_user_system_dir(dsa, dsa, Config0); 256init_per_testcase(connect_dsa_to_ecdsa, Config0) -> 257 setup_user_system_dir(dsa, ecdsa, Config0); 258init_per_testcase(connect_dsa_to_ed25519, Config0) -> 259 setup_user_system_dir(dsa, ed25519, Config0); 260init_per_testcase(connect_dsa_to_ed448, Config0) -> 261 setup_user_system_dir(dsa, ed448, Config0); 262init_per_testcase(connect_ecdsa_to_rsa_sha2, Config0) -> 263 setup_user_system_dir(ecdsa, rsa_sha2, Config0); 264init_per_testcase(connect_ecdsa_to_dsa, Config0) -> 265 setup_user_system_dir(ecdsa, dsa, Config0); 266init_per_testcase(connect_ecdsa_to_ecdsa, Config0) -> 267 setup_user_system_dir(ecdsa, ecdsa, Config0); 268init_per_testcase(connect_ecdsa_to_ed25519, Config0) -> 269 setup_user_system_dir(ecdsa, ed25519, Config0); 270init_per_testcase(connect_ecdsa_to_ed448, Config0) -> 271 setup_user_system_dir(ecdsa, ed448, Config0); 272init_per_testcase(connect_ed25519_to_rsa_sha2, Config0) -> 273 setup_user_system_dir(ed25519, rsa_sha2, Config0); 274init_per_testcase(connect_ed25519_to_dsa, Config0) -> 275 setup_user_system_dir(ed25519, dsa, Config0); 276init_per_testcase(connect_ed25519_to_ecdsa, Config0) -> 277 setup_user_system_dir(ed25519, ecdsa, Config0); 278init_per_testcase(connect_ed25519_to_ed25519, Config0) -> 279 setup_user_system_dir(ed25519, ed25519, Config0); 280init_per_testcase(connect_ed25519_to_ed448, Config0) -> 281 setup_user_system_dir(ed25519, ed448, Config0); 282init_per_testcase(connect_ed448_to_rsa_sha2, Config0) -> 283 setup_user_system_dir(ed448, rsa_sha2, Config0); 284init_per_testcase(connect_ed448_to_dsa, Config0) -> 285 setup_user_system_dir(ed448, dsa, Config0); 286init_per_testcase(connect_ed448_to_ecdsa, Config0) -> 287 setup_user_system_dir(ed448, ecdsa, Config0); 288init_per_testcase(connect_ed448_to_ed25519, Config0) -> 289 setup_user_system_dir(ed448, ed25519, Config0); 290init_per_testcase(connect_ed448_to_ed448, Config0) -> 291 setup_user_system_dir(ed448, ed448, Config0); 292 293init_per_testcase(check_dsa_disabled, Config0) -> 294 setup_default_user_system_dir(dsa, Config0); 295init_per_testcase(check_rsa_sha1_disabled, Config0) -> 296 setup_default_user_system_dir(rsa_sha1, Config0); 297 298init_per_testcase(ssh_hostkey_fingerprint_md5_implicit, Config) -> 299 init_fingerprint_testcase([md5], Config); 300 301init_per_testcase(ssh_hostkey_fingerprint_md5, Config) -> 302 init_fingerprint_testcase([md5], Config); 303 304init_per_testcase(ssh_hostkey_fingerprint_sha, Config) -> 305 init_fingerprint_testcase([sha], Config); 306 307init_per_testcase(ssh_hostkey_fingerprint_sha256, Config) -> 308 init_fingerprint_testcase([sha256], Config); 309 310init_per_testcase(ssh_hostkey_fingerprint_sha384, Config) -> 311 init_fingerprint_testcase([sha384], Config); 312 313init_per_testcase(ssh_hostkey_fingerprint_sha512, Config) -> 314 init_fingerprint_testcase([sha512], Config); 315 316init_per_testcase(ssh_hostkey_fingerprint_list , Config) -> 317 init_fingerprint_testcase([sha,md5], Config); 318 319init_per_testcase(_, Config) -> 320 Config. 321 322 323end_per_testcase(_, Config) -> 324 Config. 325 326%%%---- 327init_fingerprint_testcase(Algs, Config0) -> 328 Hashs = proplists:get_value(hashs, crypto:supports(), []), 329 case Algs -- Hashs of 330 [] -> 331 Config = lists:keydelete(watchdog, 1, Config0), 332 Dog = ct:timetrap(?TIMEOUT), 333 [{watchdog, Dog} | Config]; 334 UnsupportedAlgs -> 335 {skip,{UnsupportedAlgs,not_supported}} 336 end. 337 338%%%---------------------------------------------------------------- 339%%% Test Cases ---------------------------------------------------- 340%%%---------------------------------------------------------------- 341connect_rsa_sha2_to_rsa_sha2(Config) -> 342 try_connect(Config). 343 344connect_rsa_sha1_to_dsa(Config) -> 345 try_connect(Config). 346 347connect_rsa_sha2_to_dsa(Config) -> 348 try_connect(Config). 349 350connect_rsa_sha2_to_ecdsa(Config) -> 351 try_connect(Config). 352 353connect_rsa_sha2_to_ed25519(Config) -> 354 try_connect(Config). 355 356connect_rsa_sha2_to_ed448(Config) -> 357 try_connect(Config). 358 359connect_dsa_to_rsa_sha2(Config) -> 360 try_connect(Config). 361 362connect_dsa_to_dsa(Config) -> 363 try_connect(Config). 364 365connect_dsa_to_ecdsa(Config) -> 366 try_connect(Config). 367 368connect_dsa_to_ed25519(Config) -> 369 try_connect(Config). 370 371connect_dsa_to_ed448(Config) -> 372 try_connect(Config). 373 374connect_ecdsa_to_rsa_sha2(Config) -> 375 try_connect(Config). 376 377connect_ecdsa_to_dsa(Config) -> 378 try_connect(Config). 379 380connect_ecdsa_to_ecdsa(Config) -> 381 try_connect(Config). 382 383connect_ecdsa_to_ed25519(Config) -> 384 try_connect(Config). 385 386connect_ecdsa_to_ed448(Config) -> 387 try_connect(Config). 388 389connect_ed25519_to_rsa_sha2(Config) -> 390 try_connect(Config). 391 392connect_ed25519_to_dsa(Config) -> 393 try_connect(Config). 394 395connect_ed25519_to_ecdsa(Config) -> 396 try_connect(Config). 397 398connect_ed25519_to_ed25519(Config) -> 399 try_connect(Config). 400 401connect_ed25519_to_ed448(Config) -> 402 try_connect(Config). 403 404connect_ed448_to_rsa_sha2(Config) -> 405 try_connect(Config). 406 407connect_ed448_to_dsa(Config) -> 408 try_connect(Config). 409 410connect_ed448_to_ecdsa(Config) -> 411 try_connect(Config). 412 413connect_ed448_to_ed25519(Config) -> 414 try_connect(Config). 415 416connect_ed448_to_ed448(Config) -> 417 try_connect(Config). 418 419%%%---------------------------------------------------------------- 420check_dsa_disabled(Config) -> 421 try_connect_disabled(Config). 422 423check_rsa_sha1_disabled(Config) -> 424 try_connect_disabled(Config). 425 426 427%%%---------------------------------------------------------------- 428 429%% Check of different host keys left to later 430ssh_hostkey_fingerprint_md5_implicit(_Config) -> 431 Expected = "4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", 432 Expected = ssh:hostkey_fingerprint(ssh_hostkey(rsa)). 433 434%%-------------------------------------------------------------------- 435%% Check of different host keys left to later 436ssh_hostkey_fingerprint_md5(_Config) -> 437 Expected = "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", 438 Expected = ssh:hostkey_fingerprint(md5, ssh_hostkey(rsa)). 439 440%%-------------------------------------------------------------------- 441%% Since this kind of fingerprint is not available yet on standard 442%% distros, we do like this instead. The Expected is generated with: 443%% $ openssh-7.3p1/ssh-keygen -E sha1 -lf <file> 444%% 2048 SHA1:Soammnaqg06jrm2jivMSnzQGlmk none@example.org (RSA) 445ssh_hostkey_fingerprint_sha(_Config) -> 446 Expected = "SHA1:Soammnaqg06jrm2jivMSnzQGlmk", 447 Expected = ssh:hostkey_fingerprint(sha, ssh_hostkey(rsa)). 448 449%%-------------------------------------------------------------------- 450%% Since this kind of fingerprint is not available yet on standard 451%% distros, we do like this instead. 452ssh_hostkey_fingerprint_sha256(_Config) -> 453 Expected = "SHA256:T7F1BahkJWR7iJO8+rpzWOPbp7LZP4MlNrDExdNYOvY", 454 Expected = ssh:hostkey_fingerprint(sha256, ssh_hostkey(rsa)). 455 456%%-------------------------------------------------------------------- 457%% Since this kind of fingerprint is not available yet on standard 458%% distros, we do like this instead. 459ssh_hostkey_fingerprint_sha384(_Config) -> 460 Expected = "SHA384:QhkLoGNI4KXdPvC//HxxSCP3uTQVADqxdajbgm+Gkx9zqz8N94HyP1JmH8C4/aEl", 461 Expected = ssh:hostkey_fingerprint(sha384, ssh_hostkey(rsa)). 462 463%%-------------------------------------------------------------------- 464%% Since this kind of fingerprint is not available yet on standard 465%% distros, we do like this instead. 466ssh_hostkey_fingerprint_sha512(_Config) -> 467 Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA", 468 Expected = ssh:hostkey_fingerprint(sha512, ssh_hostkey(rsa)). 469 470%%-------------------------------------------------------------------- 471%% Since this kind of fingerprint is not available yet on standard 472%% distros, we do like this instead. 473ssh_hostkey_fingerprint_list(_Config) -> 474 Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk", 475 "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"], 476 Expected = ssh:hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)). 477 478%%-------------------------------------------------------------------- 479ssh_rsa_public_key(Config) when is_list(Config) -> 480 Datadir = proplists:get_value(pk_data_dir, Config), 481 {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")), 482 [{PubKey, Attributes1}] = ssh_file:decode(RSARawSsh2, public_key), 483 [{PubKey, Attributes1}] = ssh_file:decode(RSARawSsh2, rfc4716_key), 484 485 {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")), 486 [{PubKey, Attributes2}] = ssh_file:decode(RSARawOpenSsh, public_key), 487 [{PubKey, Attributes2}] = ssh_file:decode(RSARawOpenSsh, openssh_key), 488 489 %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh 490 %% = RSARawOpenSsh as line breakpoints may differ 491 492 EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key), 493 EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key), 494 495 [{PubKey, Attributes1}] = 496 ssh_file:decode(EncodedSSh, public_key), 497 [{PubKey, Attributes2}] = 498 ssh_file:decode(EncodedOpenSsh, public_key). 499 500%%-------------------------------------------------------------------- 501ssh_dsa_public_key(Config) when is_list(Config) -> 502 Datadir = proplists:get_value(pk_data_dir, Config), 503 504 {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")), 505 [{PubKey, Attributes1}] = ssh_file:decode(DSARawSsh2, public_key), 506 [{PubKey, Attributes1}] = ssh_file:decode(DSARawSsh2, rfc4716_key), 507 508 {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")), 509 [{PubKey, Attributes2}] = ssh_file:decode(DSARawOpenSsh, public_key), 510 [{PubKey, Attributes2}] = ssh_file:decode(DSARawOpenSsh, openssh_key), 511 512 %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh 513 %% = DSARawOpenSsh as line breakpoints may differ 514 515 EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key), 516 EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key), 517 518 [{PubKey, Attributes1}] = 519 ssh_file:decode(EncodedSSh, public_key), 520 [{PubKey, Attributes2}] = 521 ssh_file:decode(EncodedOpenSsh, public_key). 522 523%%-------------------------------------------------------------------- 524ssh_ecdsa_public_key(Config) when is_list(Config) -> 525 Datadir = proplists:get_value(pk_data_dir, Config), 526 527 {ok, ECDSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_ecdsa_pub")), 528 [{PubKey, Attributes1}] = ssh_file:decode(ECDSARawSsh2, public_key), 529 [{PubKey, Attributes1}] = ssh_file:decode(ECDSARawSsh2, rfc4716_key), 530 531 {ok, ECDSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_ecdsa_pub")), 532 [{PubKey, Attributes2}] = ssh_file:decode(ECDSARawOpenSsh, public_key), 533 [{PubKey, Attributes2}] =ssh_file:decode(ECDSARawOpenSsh, openssh_key), 534 535 %% Can not check EncodedSSh == ECDSARawSsh2 and EncodedOpenSsh 536 %% = ECDSARawOpenSsh as line breakpoints may differ 537 538 EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key), 539 EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key), 540 541 [{PubKey, Attributes1}] = 542 ssh_file:decode(EncodedSSh, public_key), 543 [{PubKey, Attributes2}] = 544 ssh_file:decode(EncodedOpenSsh, public_key). 545 546%%-------------------------------------------------------------------- 547ssh_list_public_key(Config) when is_list(Config) -> 548 DataDir = proplists:get_value(pk_data_dir, Config), 549 {Data_ssh2, Expect_ssh2} = 550 collect_binaries_expected(DataDir, rfc4716_key, 551 ["ssh2_rsa_pub", "ssh2_rsa_comment_pub", 552 "ssh2_dsa_pub", "ssh2_dsa_comment_pub", 553 "ssh2_ecdsa_pub", 554 "ssh2_subject_pub"]), 555 {Data_openssh, Expect_openssh} = 556 collect_binaries_expected(DataDir, openssh_key, 557 ["openssh_rsa_pub", "openssh_dsa_pub", "openssh_ecdsa_pub"]), 558 559 true = 560 (chk_decode(Data_openssh, Expect_openssh, openssh_key) and 561 chk_decode(Data_ssh2, Expect_ssh2, rfc4716_key) and 562 chk_decode(Data_openssh, Expect_openssh, public_key) and 563 chk_decode(Data_ssh2, Expect_ssh2, public_key) and 564 chk_encode(Expect_openssh, openssh_key) and 565 chk_encode(Expect_ssh2, rfc4716_key) 566 ). 567 568chk_encode(Data, Type) -> 569 case ssh_file:decode(ssh_file:encode(Data,Type), Type) of 570 Data-> 571 ct:log("re-encode ~p ok", [Type]), 572 true; 573 Result -> 574 ct:log("re-encode ~p FAILED~n" 575 "Got~n ~p~nExpect~n ~p~n", 576 [Type, Result, Data]), 577 false 578 end. 579 580 581chk_decode(Data, Expect, Type) -> 582 case ssh_file:decode(Data, Type) of 583 Expect -> 584 ct:log("decode ~p ok", [Type]), 585 true; 586 BadResult -> 587 ct:log("decode ~p FAILED~n" 588 "Result~n ~p~nExpect~n ~p~n" 589 "~p", 590 [Type, BadResult, Expect, 591 if 592 is_list(BadResult) -> 593 lists:foldr(fun({Key,Attrs}, Acc) -> 594 case Key of 595 #'RSAPublicKey'{} when is_list(Attrs) -> Acc; 596 {_, #'Dss-Parms'{}} when is_list(Attrs) -> Acc; 597 {#'ECPoint'{}, {namedCurve,_}} when is_list(Attrs) -> Acc; 598 _ when is_list(Attrs) -> [{bad_key,{Key,Attrs}}|Acc]; 599 _ -> [{bad_attrs,{Key,Attrs}}|Acc] 600 end; 601 (Other,Acc) -> 602 [{other,Other}|Acc] 603 end, [], BadResult); 604 true -> 605 '???' 606 end]), 607 false 608 end. 609 610 611collect_binaries_expected(Dir, Type, Files) -> 612 Bins0 = [B || F <- Files, 613 {ok,B} <- [ file:read_file(filename:join(Dir,F)) ] 614 ], 615 {list_to_binary( lists:join("\n", Bins0)), 616 lists:flatten([ssh_file:decode(B,Type) || B <- Bins0])}. 617 618%%-------------------------------------------------------------------- 619ssh_rfc4716_rsa_comment(Config) when is_list(Config) -> 620 Datadir = proplists:get_value(pk_data_dir, Config), 621 622 {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")), 623 [{#'RSAPublicKey'{} = PubKey, Attributes}] = 624 ssh_file:decode(RSARawSsh2, public_key), 625 626 Headers = proplists:get_value(headers, Attributes), 627 628 Value = proplists:get_value("Comment", Headers, undefined), 629 true = Value =/= undefined, 630 RSARawSsh2 = ssh_file:encode([{PubKey, Attributes}], rfc4716_key). 631 632%%-------------------------------------------------------------------- 633ssh_rfc4716_dsa_comment(Config) when is_list(Config) -> 634 Datadir = proplists:get_value(pk_data_dir, Config), 635 636 {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")), 637 [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] = 638 ssh_file:decode(DSARawSsh2, public_key), 639 640 Headers = proplists:get_value(headers, Attributes), 641 642 Value = proplists:get_value("Comment", Headers, undefined), 643 true = Value =/= undefined, 644 645 %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ 646 Encoded = ssh_file:encode([{PubKey, Attributes}], rfc4716_key), 647 [{PubKey, Attributes}] = 648 ssh_file:decode(Encoded, public_key). 649 650%%-------------------------------------------------------------------- 651ssh_rfc4716_rsa_subject(Config) when is_list(Config) -> 652 Datadir = proplists:get_value(pk_data_dir, Config), 653 654 {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")), 655 [{#'RSAPublicKey'{} = PubKey, Attributes}] = 656 ssh_file:decode(RSARawSsh2, public_key), 657 658 Headers = proplists:get_value(headers, Attributes), 659 660 Value = proplists:get_value("Subject", Headers, undefined), 661 true = Value =/= undefined, 662 663 %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ 664 Encoded = ssh_file:encode([{PubKey, Attributes}], rfc4716_key), 665 [{PubKey, Attributes}] = 666 ssh_file:decode(Encoded, public_key). 667 668%%-------------------------------------------------------------------- 669ssh_known_hosts(Config) when is_list(Config) -> 670 Datadir = proplists:get_value(pk_data_dir, Config), 671 672 {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")), 673 [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}, 674 {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded = 675 ssh_file:decode(SshKnownHosts, known_hosts), 676 677 Comment1 = undefined, 678 Comment2 = "foo@bar.com", 679 Comment3 = "Comment with whitespaces", 680 Comment4 = "foo@bar.com Comment with whitespaces", 681 682 Comment1 = proplists:get_value(comment, Attributes1, undefined), 683 Comment2 = proplists:get_value(comment, Attributes2), 684 Comment3 = proplists:get_value(comment, Attributes3), 685 Comment4 = proplists:get_value(comment, Attributes4), 686 687 Value1 = proplists:get_value(hostnames, Attributes1, undefined), 688 Value2 = proplists:get_value(hostnames, Attributes2, undefined), 689 true = (Value1 =/= undefined) and (Value2 =/= undefined), 690 691 Encoded = ssh_file:encode(Decoded, known_hosts), 692 Decoded = ssh_file:decode(Encoded, known_hosts). 693 694%%-------------------------------------------------------------------- 695ssh1_known_hosts(Config) when is_list(Config) -> 696 Datadir = proplists:get_value(pk_data_dir, Config), 697 698 {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")), 699 [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}] 700 = Decoded = ssh_file:decode(SshKnownHosts, known_hosts), 701 702 Value1 = proplists:get_value(hostnames, Attributes1, undefined), 703 Value2 = proplists:get_value(hostnames, Attributes2, undefined), 704 true = (Value1 =/= undefined) and (Value2 =/= undefined), 705 706 Comment ="dhopson@VMUbuntu-DSH comment with whitespaces", 707 Comment = proplists:get_value(comment, Attributes3), 708 709 Encoded = ssh_file:encode(Decoded, known_hosts), 710 Decoded = ssh_file:decode(Encoded, known_hosts). 711 712%%-------------------------------------------------------------------- 713ssh_auth_keys(Config) when is_list(Config) -> 714 Datadir = proplists:get_value(pk_data_dir, Config), 715 716 {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")), 717 [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2}, 718 {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4} 719 ] = Decoded = 720 ssh_file:decode(SshAuthKeys, auth_keys), 721 722 Value1 = proplists:get_value(options, Attributes1, undefined), 723 true = Value1 =/= undefined, 724 725 Comment1 = Comment2 = "dhopson@VMUbuntu-DSH", 726 Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces", 727 728 Comment1 = proplists:get_value(comment, Attributes1), 729 Comment2 = proplists:get_value(comment, Attributes2), 730 Comment3 = proplists:get_value(comment, Attributes3), 731 Comment4 = proplists:get_value(comment, Attributes4), 732 733 Encoded = ssh_file:encode(Decoded, auth_keys), 734 Decoded = ssh_file:decode(Encoded, auth_keys). 735 736%%-------------------------------------------------------------------- 737ssh1_auth_keys(Config) when is_list(Config) -> 738 Datadir = proplists:get_value(pk_data_dir, Config), 739 740 {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")), 741 [{#'RSAPublicKey'{}, Attributes1}, 742 {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3}, 743 {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded = 744 ssh_file:decode(SshAuthKeys, auth_keys), 745 746 Value1 = proplists:get_value(bits, Attributes2, undefined), 747 Value2 = proplists:get_value(bits, Attributes3, undefined), 748 true = (Value1 =/= undefined) and (Value2 =/= undefined), 749 750 Comment2 = Comment3 = "dhopson@VMUbuntu-DSH", 751 Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces", 752 753 undefined = proplists:get_value(comment, Attributes1, undefined), 754 Comment2 = proplists:get_value(comment, Attributes2), 755 Comment3 = proplists:get_value(comment, Attributes3), 756 Comment4 = proplists:get_value(comment, Attributes4), 757 Comment5 = proplists:get_value(comment, Attributes5), 758 759 Encoded = ssh_file:encode(Decoded, auth_keys), 760 Decoded = ssh_file:decode(Encoded, auth_keys). 761 762%%-------------------------------------------------------------------- 763ssh_openssh_key_with_comment(Config) when is_list(Config) -> 764 Datadir = proplists:get_value(pk_data_dir, Config), 765 766 {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")), 767 [{{_, #'Dss-Parms'{}}, _}] = ssh_file:decode(DSARawOpenSsh, openssh_key). 768 769%%-------------------------------------------------------------------- 770ssh_openssh_key_long_header(Config) when is_list(Config) -> 771 Datadir = proplists:get_value(pk_data_dir, Config), 772 773 {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")), 774 [{#'RSAPublicKey'{}, _}] = Decoded = ssh_file:decode(RSARawOpenSsh, public_key), 775 776 Encoded = ssh_file:encode(Decoded, rfc4716_key), 777 Decoded = ssh_file:decode(Encoded, rfc4716_key). 778 779%%%---------------------------------------------------------------- 780%%% Test case helpers 781%%%---------------------------------------------------------------- 782%% Should use stored keys instead 783ssh_hostkey(rsa) -> 784 [{PKdecoded,_}] = 785 ssh_file:decode( 786 <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYXcYmsyJBstl4EfFYzfQJmSiUE162zvSGSoMYybShYOI6rnnyvvihfw8Aml+2gZ716F2tqG48FQ/yPZEGWNPMrCejPpJctaPWhpNdNMJ8KFXSEgr5bY2mEpa19DHmuDeXKzeJJ+X7s3fVdYc4FMk5731KIW6Huf019ZnTxbx0VKG6b1KAJBg3vpNsDxEMwQ4LFMB0JHVklOTzbxmpaeULuIxvl65A+eGeFVeo2Q+YI9UnwY1vSgmc9Azwy8Ie9Z0HpQBN5I7Uc5xnknT8V6xDhgNfXEfzsgsRdDfZLECt1WO/1gP9wkosvAGZWt5oG8pbNQWiQdFq536ck8WQD9WD none@example.org">>, 787 public_key), 788 PKdecoded. 789 790%%%---------------------------------------------------------------- 791chk_known_hosts(Config) -> 792 PrivDir = proplists:get_value(priv_dir, Config), 793 794 DataDir = filename:join(proplists:get_value(data_dir,Config), "new_format"), 795 SysDir = filename:join(PrivDir, "chk_known_hosts_sys_dir"), 796 ssh_test_lib:setup_all_host_keys(DataDir, SysDir), 797 798 UsrDir = filename:join(PrivDir, "chk_known_hosts_usr_dir"), 799 file:make_dir(UsrDir), 800 KnownHostsFile = filename:join(UsrDir, "known_hosts"), 801 802 DaemonOpts = [{system_dir, SysDir}, 803 {user_dir, UsrDir}, 804 {password, "bar"}], 805 806 UserOpts = [{user_dir, UsrDir}, 807 {user, "foo"}, 808 {password, "bar"}, 809 {silently_accept_hosts, true}, 810 {user_interaction, false} 811 ], 812 813 {_Pid1, Host1, Port1} = ssh_test_lib:daemon(DaemonOpts), 814 {_Pid2, Host2, Port2} = ssh_test_lib:daemon(DaemonOpts), 815 816 _C1 = ssh_test_lib:connect(Host1, Port1, UserOpts), 817 {ok,KnownHosts1} = file:read_file(KnownHostsFile), 818 Sz1 = byte_size(KnownHosts1), 819 ct:log("~p bytes KnownHosts1 = ~p", [Sz1, KnownHosts1]), 820 821 _C2 = ssh_test_lib:connect(Host2, Port2, UserOpts), 822 {ok,KnownHosts2} = file:read_file(KnownHostsFile), 823 Sz2 = byte_size(KnownHosts2), 824 ct:log("~p bytes KnownHosts2 = ~p", [Sz2, KnownHosts2]), 825 826 %% Check that 2nd is appended after the 1st: 827 <<KnownHosts1:Sz1/binary, _/binary>> = KnownHosts2, 828 829 %% Check that there are exactly two NLs: 830 2 = lists:foldl(fun($\n, Sum) -> Sum + 1; 831 (_, Sum) -> Sum 832 end, 0, binary_to_list(KnownHosts2)), 833 834 %% Check that at least one NL terminates both two lines: 835 <<_:(Sz1-1)/binary, $\n, _:(Sz2-Sz1-1)/binary, $\n>> = KnownHosts2. 836 837 838%%%---------------------------------------------------------------- 839try_connect({skip,Reson}) -> 840 {skip,Reson}; 841try_connect(Config) -> 842 SystemDir = proplists:get_value(system_dir, Config), 843 UserDir = proplists:get_value(user_dir, Config), 844 ClientOpts = proplists:get_value(client_opts, Config, []), 845 DaemonOpts = proplists:get_value(daemon_opts, Config, []), 846 847 ssh_dbg:start(fun ct:log/2), ssh_dbg:on([alg]), 848 {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, 849 {user_dir, UserDir} 850 | DaemonOpts]), 851 852 C = ssh_test_lib:connect(Host, Port, [{user_dir, UserDir}, 853 {silently_accept_hosts, true}, 854 {user_interaction, false} 855 | ClientOpts]), 856 ssh:close(C), 857 ssh_dbg:stop(), 858 ssh:stop_daemon(Pid). 859 860 861try_connect_disabled(Config) -> 862 try try_connect(Config) 863 of _ -> {fail, "non-default algorithm accepted"} 864 catch error:{badmatch,{error,"Service not available"}} -> ok 865 end. 866 867%%%---------------------------------------------------------------- 868%%% Local --------------------------------------------------------- 869%%%---------------------------------------------------------------- 870setup_user_system_dir(ClientAlg, ServerAlg, Config) -> 871 case supported(public_key, ClientAlg) andalso supported(public_key, ServerAlg) of 872 true -> 873 try 874 setup_dirs(ClientAlg, ServerAlg, Config) 875 of 876 {ok, {SystemDir,UserDir}} -> 877 ModAlgs = [{preferred_algorithms, 878 [{public_key, lists:usort([alg(ClientAlg), alg(ServerAlg)])}] 879 }], 880 [{system_dir,SystemDir}, 881 {user_dir,UserDir} 882 | extend_optsL([daemon_opts,client_opts], ModAlgs, Config)] 883 catch 884 error:{badmatch,{error,enoent}}:S -> 885 ct:log("~p:~p Stack:~n~p", [?MODULE,?LINE,S]), 886 {skip, no_key_file_found} 887 end; 888 889 false -> 890 {skip, unsupported_algorithm} 891 end. 892 893 894setup_default_user_system_dir(ClientAlg, Config) -> 895 ServerAlg = ecdsa, 896 case default(public_key, ClientAlg) of 897 false -> 898 case supported(public_key, ClientAlg) of 899 true -> 900 case supported(public_key, ServerAlg) of 901 true -> 902 try 903 setup_dirs(ClientAlg, ServerAlg, Config) 904 of 905 {ok, {SystemDir,UserDir}} -> 906 ModAlgs = [{modify_algorithms, 907 [{append,[{public_key,[alg(ServerAlg)]}]}, 908 {rm, [{public_key,[alg(ClientAlg)|inv_algs(ClientAlg)]}]} 909 ]}], 910 [{system_dir,SystemDir}, 911 {user_dir,UserDir} 912 | extend_optsL([daemon_opts,client_opts], ModAlgs, Config)] 913 catch 914 error:{badmatch,{error,enoent}}:S -> 915 ct:log("~p:~p Stack:~n~p", [?MODULE,?LINE,S]), 916 {skip, no_key_file_found} 917 end; 918 false -> 919 {skip, unsupported_server_algorithm} 920 end; 921 false -> 922 {skip, unsupported_client_algorithm} 923 end; 924 true -> 925 {fail, disabled_algorithm_present} 926 end. 927 928 929setup_dirs(ClientAlg, ServerAlg, Config) -> 930 PrivDir = proplists:get_value(priv_dir, Config), 931 KeySrcDir = proplists:get_value(key_src_dir, Config), 932 Fmt = proplists:get_value(fmt, Config), 933 934 System = lists:concat(["system_", ClientAlg, "_", ServerAlg, "_", Fmt]), 935 SystemDir = filename:join(PrivDir, System), 936 file:make_dir(SystemDir), 937 938 User = lists:concat(["user_", ClientAlg, "_", ServerAlg, "_", Fmt]), 939 UserDir = filename:join(PrivDir, User), 940 file:make_dir(UserDir), 941 942 HostSrcFile = filename:join(KeySrcDir, file(src,host,ServerAlg)), 943 HostDstFile = filename:join(SystemDir, file(dst,host,ServerAlg)), 944 945 UserSrcFile = filename:join(KeySrcDir, file(src,user,ClientAlg)), 946 UserDstFile = filename:join(UserDir, file(dst,user,ClientAlg)), 947 948 UserPubSrcFile = filename:join(KeySrcDir, file(src,user,ClientAlg)++".pub"), 949 AuthorizedKeys = filename:join(UserDir, "authorized_keys"), 950 951 ct:log("UserSrcFile = ~p~nUserDstFile = ~p", [UserSrcFile, UserDstFile]), 952 {ok,_} = file:copy(UserSrcFile, UserDstFile), 953 ct:log("UserPubSrcFile = ~p~nAuthorizedKeys = ~p", [UserPubSrcFile, AuthorizedKeys]), 954 {ok,_} = file:copy(UserPubSrcFile, AuthorizedKeys), 955 ct:log("HostSrcFile = ~p~nHostDstFile = ~p", [HostSrcFile, HostDstFile]), 956 {ok,_} = file:copy(HostSrcFile, HostDstFile), 957 958 ct:log("SystemDir = ~p~nUserDir = ~p", [SystemDir,UserDir]), 959 {ok, {SystemDir,UserDir}}. 960 961%%%---------------------------------------------------------------- 962file( _, host, dsa) -> "ssh_host_dsa_key"; 963file( _, host, ecdsa) -> "ssh_host_ecdsa_key"; 964file( _, host, ed25519) -> "ssh_host_ed25519_key"; 965file( _, host, ed448) -> "ssh_host_ed448_key"; 966file( _, host, rsa_sha2)-> "ssh_host_rsa_key"; 967file(src, host, rsa_sha1)-> "ssh_host_rsa_key"; 968file(dst, host, rsa_sha1)-> "ssh_host_rsa_key"; 969file( _, user, dsa) -> "id_dsa"; 970file( _, user, ecdsa) -> "id_ecdsa"; 971file( _, user, ed25519) -> "id_ed25519"; 972file( _, user, ed448) -> "id_ed448"; 973file( _, user, rsa_sha2)-> "id_rsa"; 974file(src, user, rsa_sha1)-> "id_rsa"; 975file(dst, user, rsa_sha1)-> "id_rsa". 976 977alg(dsa) -> 'ssh-dss'; 978alg(ecdsa) -> 'ecdsa-sha2-nistp256'; 979alg(ed25519) -> 'ssh-ed25519'; 980alg(ed448) -> 'ssh-ed448'; 981alg(rsa_sha2)-> 'rsa-sha2-256'; 982alg(rsa_sha1)-> 'ssh-rsa'. 983 984inv_algs(rsa_sha1) -> algs(rsa_sha2); 985inv_algs(_) -> []. 986 987algs(dsa) -> ['ssh-dss']; 988algs(ecdsa) -> ['ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-521']; 989algs(ed25519) -> ['ssh-ed25519']; 990algs(ed448) -> ['ssh-ed448']; 991algs(rsa_sha2)-> ['rsa-sha2-256', 'rsa-sha2-384', 'rsa-sha2-512']; 992algs(rsa_sha1)-> ['ssh-rsa']; 993algs(A) -> [A]. 994 995 996 997default(Type, Alg) -> listed(algs(Alg), ssh_transport:default_algorithms(Type)). 998 999supported(Type, Alg) -> listed(algs(Alg), 1000 try 1001 ssh_transport:supported_algorithms(Type) 1002 catch 1003 error:function_clause -> crypto:supports(Type) 1004 end). 1005 1006listed(As, L) -> lists:any(fun(A) -> lists:member(A,L) end, 1007 As). 1008 1009 1010