diff --git a/Cargo.lock b/Cargo.lock index ad1d28d3..7f03988d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,8 +584,12 @@ name = "bitwarden-ssh" version = "1.0.0" dependencies = [ "bitwarden-error", + "ed25519", + "pem-rfc7468", + "pkcs8", "rand", "rand_chacha", + "rsa", "serde", "ssh-key", "thiserror 1.0.69", @@ -1398,6 +1402,7 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ + "pkcs8", "signature", ] @@ -2580,24 +2585,8 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ - "ecdsa", "elliptic-curve", "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core", - "sha2", ] [[package]] @@ -2711,6 +2700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest", + "hmac", ] [[package]] @@ -2751,6 +2741,21 @@ dependencies = [ "spki", ] +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der", + "pbkdf2", + "scrypt", + "sha2", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -2758,6 +2763,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", + "pkcs5", + "rand_core", "spki", ] @@ -3245,6 +3252,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3321,6 +3337,17 @@ dependencies = [ "syn", ] +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "sec1" version = "0.7.3" @@ -3674,12 +3701,8 @@ dependencies = [ "bcrypt-pbkdf", "ed25519-dalek", "num-bigint-dig", - "p256", - "p384", - "p521", "rand_core", "rsa", - "sec1", "sha2", "signature", "ssh-cipher", diff --git a/crates/bitwarden-ssh/Cargo.toml b/crates/bitwarden-ssh/Cargo.toml index f1f1095e..e17f53e3 100644 --- a/crates/bitwarden-ssh/Cargo.toml +++ b/crates/bitwarden-ssh/Cargo.toml @@ -3,6 +3,7 @@ name = "bitwarden-ssh" description = """ Internal crate for the bitwarden crate. Do not use. """ +exclude = ["/resources"] version.workspace = true authors.workspace = true @@ -22,14 +23,17 @@ wasm = [ [dependencies] bitwarden-error = { workspace = true } -rand = "0.8.5" +ed25519 = { version = ">=2.2.3, <3.0", features = ["pkcs8"] } +pem-rfc7468 = "0.7.0" +pkcs8 = { version = ">=0.10.2, <0.11", features = ["encryption"] } +rand = ">=0.8.5, <0.9" +rsa = ">=0.9.2, <0.10" serde.workspace = true -ssh-key = { version = "0.6.7", features = [ +ssh-key = { version = ">=0.6.7, <0.7", features = [ "ed25519", "encryption", "rsa", - "getrandom", -] } +], default-features = false } thiserror = { workspace = true } tsify-next = { workspace = true, optional = true } wasm-bindgen = { workspace = true, optional = true } diff --git a/crates/bitwarden-ssh/tests/ed25519_key b/crates/bitwarden-ssh/resources/generator/ed25519_key similarity index 100% rename from crates/bitwarden-ssh/tests/ed25519_key rename to crates/bitwarden-ssh/resources/generator/ed25519_key diff --git a/crates/bitwarden-ssh/tests/rsa3072_key b/crates/bitwarden-ssh/resources/generator/rsa3072_key similarity index 100% rename from crates/bitwarden-ssh/tests/rsa3072_key rename to crates/bitwarden-ssh/resources/generator/rsa3072_key diff --git a/crates/bitwarden-ssh/tests/rsa4096_key b/crates/bitwarden-ssh/resources/generator/rsa4096_key similarity index 100% rename from crates/bitwarden-ssh/tests/rsa4096_key rename to crates/bitwarden-ssh/resources/generator/rsa4096_key diff --git a/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted b/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted new file mode 100644 index 00000000..9cf518f8 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRQzzQ8nQEouF1FMSHkPx1nejNCzF7g +Yb8MHXLdBFM0uJkWs0vzgLJkttts2eDv3SHJqIH6qHpkLtEvgMXE5WcaAAAAoOO1BebjtQ +XmAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDPNDydASi4XUUx +IeQ/HWd6M0LMXuBhvwwdct0EUzS4mRazS/OAsmS222zZ4O/dIcmogfqoemQu0S+AxcTlZx +oAAAAhAKnIXk6H0Hs3HblklaZ6UmEjjdE/0t7EdYixpMmtpJ4eAAAAB3Rlc3RrZXk= +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted.pub b/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted.pub new file mode 100644 index 00000000..75e08b88 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ecdsa_openssh_unencrypted.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDPNDydASi4XUUxIeQ/HWd6M0LMXuBhvwwdct0EUzS4mRazS/OAsmS222zZ4O/dIcmogfqoemQu0S+AxcTlZxo= testkey diff --git a/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted b/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted new file mode 100644 index 00000000..d3244a3d --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAUTNb0if +fqsoqtfv70CfukAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHGs3Uw3eyqnFjBI +2eb7Qto4KVc34ZdnBac59Bab54BLAAAAkPA6aovfxQbP6FoOfaRH6u22CxqiUM0bbMpuFf +WETn9FLaBE6LjoHH0ZI5rzNjJaQUNfx0cRcqsIrexw8YINrdVjySmEqrl5hw8gpgy0gGP5 +1Y6vKWdHdrxJCA9YMFOfDs0UhPfpLKZCwm2Sg+Bd8arlI8Gy7y4Jj/60v2bZOLhD2IZQnK +NdJ8xATiIINuTy4g== +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted.pub b/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted.pub new file mode 100644 index 00000000..1188fa43 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_openssh_encrypted.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHGs3Uw3eyqnFjBI2eb7Qto4KVc34ZdnBac59Bab54BL testkey diff --git a/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted b/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted new file mode 100644 index 00000000..08184f31 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAyQo22TXXNqvF+L8jUSSNeu8UqrsDjvf9pwIwDC9ML6gAAAJDSHpL60h6S ++gAAAAtzc2gtZWQyNTUxOQAAACAyQo22TXXNqvF+L8jUSSNeu8UqrsDjvf9pwIwDC9ML6g +AAAECLdlFLIJbEiFo/f0ROdXMNZAPHGPNhvbbftaPsUZEjaDJCjbZNdc2q8X4vyNRJI167 +xSquwOO9/2nAjAML0wvqAAAAB3Rlc3RrZXkBAgMEBQY= +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted.pub b/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted.pub new file mode 100644 index 00000000..5c398822 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_openssh_unencrypted.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJCjbZNdc2q8X4vyNRJI167xSquwOO9/2nAjAML0wvq testkey diff --git a/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted b/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted new file mode 100644 index 00000000..09eb7286 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFECAQEwBQYDK2VwBCIEIDY6/OAdDr3PbDss9NsLXK4CxiKUvz5/R9uvjtIzj4Sz +gSEAxsxm1xpZ/4lKIRYm0JrJ5gRZUh7H24/YT/0qGVGzPa0= +-----END PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted.pub b/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted.pub new file mode 100644 index 00000000..40997e18 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_pkcs8_unencrypted.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbMZtcaWf+JSiEWJtCayeYEWVIex9uP2E/9KhlRsz2t diff --git a/crates/bitwarden-ssh/resources/import/ed25519_putty_openssh_unencrypted b/crates/bitwarden-ssh/resources/import/ed25519_putty_openssh_unencrypted new file mode 100644 index 00000000..aa9c01b8 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/ed25519_putty_openssh_unencrypted @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz +c2gtZWQyNTUxOQAAACDp0/9zFBCyZs5BFqXCJN5i1DTanzPGHpUeo2LP8FmQ9wAA +AKCyIXPqsiFz6gAAAAtzc2gtZWQyNTUxOQAAACDp0/9zFBCyZs5BFqXCJN5i1DTa +nzPGHpUeo2LP8FmQ9wAAAEDQioomhjmD+sh2nsxfQLJ5YYGASNUAlUZHe9Jx0p47 +H+nT/3MUELJmzkEWpcIk3mLUNNqfM8YelR6jYs/wWZD3AAAAEmVkZHNhLWtleS0y +MDI0MTExOAECAwQFBgcICQoL +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted b/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted new file mode 100644 index 00000000..bb7bbd85 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted @@ -0,0 +1,39 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABApatKZWf +0kXnaSVhty/RaKAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC/v18xGP3q +zRV9iWqyiuwHZ4GpC4K2NO2/i2Yv5A3/bnal7CmiMh/S78lphgxcWtFkwrwlb321FmdHBv +6KOW+EzSiPvmsdkkbpfBXB3Qf2SlhZOZZ7lYeu8KAxL3exvvn8O1GGlUjXGUrFgmC60tHW +DBc1Ncmo8a2dwDLmA/sbLa8su2dvYEFmRg1vaytLDpkn8GS7zAxrUl/g0W2RwkPsByduUz +iQuX90v9WAy7MqOlwBRq6t5o8wdDBVODe0VIXC7N1OS42YUsKF+N0XOnLiJrIIKkXpahMD +pKZHeHQAdUQzsJVhKoLJR8DNDTYyhnJoQG7Q6m2gDTca9oAWvsBiNoEwCvwrt7cDNCz/Gs +lH9HXQgfWcVXn8+fuZgvjO3CxUI16Ev33m0jWoOKJcgK/ZLRnk8SEvsJ8NO32MeR/qUb7I +N/yUcDmPMI/3ecQsakF2cwNzHkyiGVo//yVTpf+vk8b89L+GXbYU5rtswtc2ZEGsQnUkao +NqS8mHqhWQBUkAAAWArmugDAR1KlxY8c/esWbgQ4oP/pAQApehDcFYOrS9Zo78Os4ofEd1 +HkgM7VG1IJafCnn+q+2VXD645zCsx5UM5Y7TcjYDp7reM19Z9JCidSVilleRedTj6LTZx1 +SvetIrTfr81SP6ZGZxNiM0AfIZJO5vk+NliDdbUibvAuLp3oYbzMS3syuRkJePWu+KSxym +nm2+88Wku94p6SIfGRT3nQsMfLS9x6fGQP5Z71DM91V33WCVhrBnvHgNxuAzHDZNfzbPu9 +f2ZD1JGh8azDPe0XRD2jZTyd3Nt+uFMcwnMdigTXaTHExEFkTdQBea1YoprIG56iNZTSoU +/RwE4A0gdrSgJnh+6p8w05u+ia0N2WSL5ZT9QydPhwB8pGHuGBYoXFcAcFwCnIAExPtIUh +wLx1NfC/B2MuD3Uwbx96q5a7xMTH51v0eQDdY3mQzdq/8OHHn9vzmEfV6mxmuyoa0Vh+WG +l2WLB2vD5w0JwRAFx6a3m/rD7iQLDvK3UiYJ7DVz5G3/1w2m4QbXIPCfI3XHU12Pye2a0m +/+/wkS4/BchqB0T4PJm6xfEynXwkEolndf+EvuLSf53XSJ2tfeFPGmmCyPoy9JxCce7wVk +FB/SJw6LXSGUO0QA6vzxbzLEMNrqrpcCiUvDGTA6jds0HnSl8hhgMuZOtQDbFoovIHX0kl +I5pD5pqaUNvQ3+RDFV3qdZyDntaPwCNJumfqUy46GAhYVN2O4p0HxDTs4/c2rkv+fGnG/P +8wc7ACz3QNdjb7XMrW3/vNuwrh/sIjNYM2aiVWtRNPU8bbSmc1sYtpJZ5CsWK1TNrDrY6R +OV89NjBoEC5OXb1c75VdN/jSssvn72XIHjkkDEPboDfmPe889VHfsVoBm18uvWPB4lffdm +4yXAr+Cx16HeiINjcy6iKym2p4ED5IGaSXlmw/6fFgyh2iF7kZTnHawVPTqJNBVMaBRvHn +ylMBLhhEkrXqW43P4uD6l0gWCAPBczcSjHv3Yo28ExtI0QKNk/Uwd2q2kxFRWCtqUyQkrF +KG9IK+ixqstMo+xEb+jcCxCswpJitEIrDOXd51sd7PjCGZtAQ6ycpOuFfCIhwxlBUZdf2O +kM/oKqN/MKMDk+H/OVl8XrLalBOXYDllW+NsL8W6F8DMcdurpQ8lCJHHWBgOdNd62STdvZ +LBf7v8OIrC6F0bVGushsxb7cwGiUrjqUfWjhZoKx35V0dWBcGx7GvzARkvSUM22q14lc7+ +XTP0qC8tcRQfRbnBPJdmnbPDrJeJcDv2ZdbAPdzf2C7cLuuP3mNwLCrLUc7gcF/xgH+Xtd +6KOvzt2UuWv5+cqWOsNspG+lCY0P11BPhlMvmZKO8RGVGg7PKAatG4mSH4IgO4DN2t7U9B +j+v2jq2z5O8O4yJ8T2kWnBlhWzlBoL+R6aaat421f0v+tW/kEAouBQob5I0u1VLB2FkpZE +6tOCK47iuarhf/86NtlPfCM9PdWJQOKcYQ8DCQhp5Lvgd0Vj3WzY+BISDdB2omGRhLUly/ +i40YPASAVnWvgqpCQ4E3rs4DWI/kEcvQH8zVq2YoRa6fVrVf1w/GLFC7m/wkxw8fDfZgMS +Mu+ygbFa9H3aOSZMpTXhdssbOhU70fZOe6GWY9kLBNV4trQeb/pRdbEbMtEmN5TLESgwLA +43dVdHjvpZS677FN/d9+q+pr0Xnuc2VdlXkUyOyv1lFPJIN/XIotiDTnZ3epQQ1zQ3mx32 +8Op2EVgFWpwNmGXJ1zCCA6loUG7e4W/iXkKQxTvOM0fmE4a1Y387GDwJ+pZevYOIOYTkTa +l5jM/6Wm3pLNyE8Ynw3OX0T/p9TO1i3DlXXE/LzcWJFFXAQMo+kc+GlXqjP7K7c6xjQ6vx +2MmKBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted.pub b/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted.pub new file mode 100644 index 00000000..d37f573b --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_openssh_encrypted.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/v18xGP3qzRV9iWqyiuwHZ4GpC4K2NO2/i2Yv5A3/bnal7CmiMh/S78lphgxcWtFkwrwlb321FmdHBv6KOW+EzSiPvmsdkkbpfBXB3Qf2SlhZOZZ7lYeu8KAxL3exvvn8O1GGlUjXGUrFgmC60tHWDBc1Ncmo8a2dwDLmA/sbLa8su2dvYEFmRg1vaytLDpkn8GS7zAxrUl/g0W2RwkPsByduUziQuX90v9WAy7MqOlwBRq6t5o8wdDBVODe0VIXC7N1OS42YUsKF+N0XOnLiJrIIKkXpahMDpKZHeHQAdUQzsJVhKoLJR8DNDTYyhnJoQG7Q6m2gDTca9oAWvsBiNoEwCvwrt7cDNCz/GslH9HXQgfWcVXn8+fuZgvjO3CxUI16Ev33m0jWoOKJcgK/ZLRnk8SEvsJ8NO32MeR/qUb7IN/yUcDmPMI/3ecQsakF2cwNzHkyiGVo//yVTpf+vk8b89L+GXbYU5rtswtc2ZEGsQnUkaoNqS8mHqhWQBUk= testkey diff --git a/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted b/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted new file mode 100644 index 00000000..0d2692e1 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAYEAtVIe0gnPtD6299/roT7ntZgVe+qIqIMIruJdI2xTanLGhNpBOlzg +WqokbQK+aXATcaB7iQL1SPxIWV2M4jEBQbZuimIgDQvKbJ4TZPKEe1VdsrfuIo+9pDK7cG +Kc+JiWhKjqeTRMj91/qR1fW5IWOUyE1rkwhTNkwJqtYKZLVmd4TXtQsYMMC+I0cz4krfk1 +Yqmaae/gj12h8BvE3Y+Koof4JoLsqPufH+H/bVEayv63RyAQ1/tUv9l+rwJ+svWV4X3zf3 +z40hGF43L/NGl90Vutbn7b9G/RgEdiXyLZciP3XbWbLUM+r7mG9KNuSeoixe5jok15UKqC +XXxVb5IEZ73kaubSfz9JtsqtKG/OjOq6Fbl3Ky7kjvJyGpIvesuSInlpzPXqbLUCLJJfOA +PUZ1wi8uuuRNePzQBMMhq8UtAbB2Dy16d+HlgghzQ00NxtbQMfDZBdApfxm3shIxkUcHzb +DSvriHVaGGoOkmHPAmsdMsMiekuUMe9ljdOhmdTxAAAFgF8XjBxfF4wcAAAAB3NzaC1yc2 +EAAAGBALVSHtIJz7Q+tvff66E+57WYFXvqiKiDCK7iXSNsU2pyxoTaQTpc4FqqJG0Cvmlw +E3Gge4kC9Uj8SFldjOIxAUG2bopiIA0LymyeE2TyhHtVXbK37iKPvaQyu3BinPiYloSo6n +k0TI/df6kdX1uSFjlMhNa5MIUzZMCarWCmS1ZneE17ULGDDAviNHM+JK35NWKpmmnv4I9d +ofAbxN2PiqKH+CaC7Kj7nx/h/21RGsr+t0cgENf7VL/Zfq8CfrL1leF98398+NIRheNy/z +RpfdFbrW5+2/Rv0YBHYl8i2XIj9121my1DPq+5hvSjbknqIsXuY6JNeVCqgl18VW+SBGe9 +5Grm0n8/SbbKrShvzozquhW5dysu5I7ychqSL3rLkiJ5acz16my1AiySXzgD1GdcIvLrrk +TXj80ATDIavFLQGwdg8tenfh5YIIc0NNDcbW0DHw2QXQKX8Zt7ISMZFHB82w0r64h1Whhq +DpJhzwJrHTLDInpLlDHvZY3ToZnU8QAAAAMBAAEAAAGAEL3wpRWtVTf+NnR5QgX4KJsOjs +bI0ABrVpSFo43uxNMss9sgLzagq5ZurxcUBFHKJdF63puEkPTkbEX4SnFaa5of6kylp3a5 +fd55rXY8F9Q5xtT3Wr8ZdFYP2xBr7INQUJb1MXRMBnOeBDw3UBH01d0UHexzB7WHXcZacG +Ria+u5XrQebwmJ3PYJwENSaTLrxDyjSplQy4QKfgxeWNPWaevylIG9vtue5Xd9WXdl6Szs +ONfD3mFxQZagPSIWl0kYIjS3P2ZpLe8+sakRcfci8RjEUP7U+QxqY5VaQScjyX1cSYeQLz +t+/6Tb167aNtQ8CVW3IzM2EEN1BrSbVxFkxWFLxogAHct06Kn87nPn2+PWGWOVCBp9KheO +FszWAJ0Kzjmaga2BpOJcrwjSpGopAb1YPIoRPVepVZlQ4gGwy5gXCFwykT9WTBoJfg0BMQ +r3MSNcoc97eBomIWEa34K0FuQ3rVjMv9ylfyLvDBbRqTJ5zebeOuU+yCQHZUKk8klRAAAA +wAsToNZvYWRsOMTWQom0EW1IHzoL8Cyua+uh72zZi/7enm4yHPJiu2KNgQXfB0GEEjHjbo +9peCW3gZGTV+Ee+cAqwYLlt0SMl/VJNxN3rEG7BAqPZb42Ii2XGjaxzFq0cliUGAdo6UEd +swU8d2I7m9vIZm4nDXzsWOBWgonTKBNyL0DQ6KNOGEyj8W0BTCm7Rzwy7EKzFWbIxr4lSc +vDrJ3t6kOd7jZTF58kRMT0nxR0bf43YzF/3/qSvLYhQm/OOAAAAMEA2F6Yp8SrpQDNDFxh +gi4GeywArrQO9r3EHjnBZi/bacxllSzCGXAvp7m9OKC1VD2wQP2JL1VEIZRUTuGGT6itrm +QpX8OgoxlEJrlC5W0kHumZ3MFGd33W11u37gOilmd6+VfVXBziNG2rFohweAgs8X+Sg5AA +nIfMV6ySXUlvLzMHpGeKRRnnQq9Cwn4rDkVQENLd1i4e2nWFhaPTUwVMR8YuOT766bywr3 +7vG1PQLF7hnf2c/oPHAru+XD9gJWs5AAAAwQDWiB2G23F4Tvq8FiK2mMusSjQzHupl83rm +o3BSNRCvCjaLx6bWhDPSA1edNEF7VuP6rSp+i+UfSORHwOnlgnrvtcJeoDuA72hUeYuqD/ +1C9gghdhKzGTVf/IGTX1tH3rn2Gq9TEyrJs/ITcoOyZprz7VbaD3bP/NEER+m1EHi2TS/3 +SXQEtRm+IIBwba+QLUcsrWdQyIO+1OCXywDrAw50s7tjgr/goHgXTcrSXaKcIEOlPgBZH3 +YPuVuEtRYgX3kAAAAHdGVzdGtleQECAwQ= +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted.pub b/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted.pub new file mode 100644 index 00000000..9ec8fec5 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_openssh_unencrypted.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1Uh7SCc+0Prb33+uhPue1mBV76oiogwiu4l0jbFNqcsaE2kE6XOBaqiRtAr5pcBNxoHuJAvVI/EhZXYziMQFBtm6KYiANC8psnhNk8oR7VV2yt+4ij72kMrtwYpz4mJaEqOp5NEyP3X+pHV9bkhY5TITWuTCFM2TAmq1gpktWZ3hNe1CxgwwL4jRzPiSt+TViqZpp7+CPXaHwG8Tdj4qih/gmguyo+58f4f9tURrK/rdHIBDX+1S/2X6vAn6y9ZXhffN/fPjSEYXjcv80aX3RW61uftv0b9GAR2JfItlyI/ddtZstQz6vuYb0o25J6iLF7mOiTXlQqoJdfFVvkgRnveRq5tJ/P0m2yq0ob86M6roVuXcrLuSO8nIaki96y5IieWnM9epstQIskl84A9RnXCLy665E14/NAEwyGrxS0BsHYPLXp34eWCCHNDTQ3G1tAx8NkF0Cl/GbeyEjGRRwfNsNK+uIdVoYag6SYc8Cax0ywyJ6S5Qx72WN06GZ1PE= testkey diff --git a/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted new file mode 100644 index 00000000..e84d1f07 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted @@ -0,0 +1,42 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted.pub b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted.pub new file mode 100644 index 00000000..f3c1b15f --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_encrypted.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCcHkc0xfH4w9aW41S9M/BfancSY4QPc2O4G1cRjFfK8QrLEGDA7NiHtoEML0afcurRXD3NVxuKaAns0w6EoS4CjzXUqVHTLA4SUyuapr8k0Eu2xOpbCwC3jDovhckoKloq7BvE6rC2i5wjSMadtIJKt/dqWI3HLjUMz1BxQJAU/qAbicj1SFZSjA/MubVBzcq93XOvByMtlIFu7wami3FTc37rVkGeUFHtK8ZbvG3n1aaTF79bBgSPuoq5BfcMdGr4WfQyGQzgse4v4hQ8yKYrtE0jo0kf06hEORimwOIU/W5IH1r+/xFs7qGKcPnFSZRIFv5LfMPTo8b+OsBRflosyfUumDEX97GZE7DSQl0EJzNvWeKwl7dQ8RUJTkbph2CjrxY77DFim+165Uj/WRr4uq2qMNhA2xNSD19+TA6AHdpGw4WZd37q2/n+EddlaJEH8MzpgtHNG9MiYh5ScZ+AG0QugflozJcQNc7n8N9Lpu1sRoejV5RhurHg/TYwVK8= testkey diff --git a/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted new file mode 100644 index 00000000..0bfe2bc5 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +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 +QjDEB37RqGZxqyIx8V2ZYwU= +-----END PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted.pub b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted.pub new file mode 100644 index 00000000..a3e04eed --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_pkcs8_unencrypted.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCn4+QiJojZ9mgc9KYJIvDWGaz4qFhf0CButg6L8zEoHKwuiN+mqcEciCCOa9BNiJmm8NTTehZvrrglGG59zIbqYtDAHjVn+vtb49xPzIv+M651Yqj08lIbR9tEIHKCq7aH8GlDm8NgG9EzJGjlL7okQym4TH1MHl+s4mUyr/qb2unlZBDixAQsphU8iCLftukWCIkmQg4CSj1Gh3WbBlZ+EX5eW0EXuAw4XsSbBTWV9CHRowVIpYqPvEYSpHsoCjEcd988p19hpiGknA0J4z7JfUlNgyT/1chb8GCTDT+2DCBRApbsIg6TOBVS+PR6emAQ3eZzUW0+3/oRM4ip0ujltQy8uU6gvYIAqx5wXGMThVpZcUgahKiSsVo/s4b84iMe4DG3W8jz4qi6yyNv0VedEzPUZ1lXd1GJFoy9uKNuSTe+1ksicAcluZN6LuNsPHcPxFCzOcmoNnVXEKAXInt+ys//5CDVasroZSAHZnDjUD4oNsLI3VIOnGxgXrkwSH0= testkey diff --git a/crates/bitwarden-ssh/resources/import/rsa_putty_openssh_unencrypted b/crates/bitwarden-ssh/resources/import/rsa_putty_openssh_unencrypted new file mode 100644 index 00000000..bbb8edfe --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_putty_openssh_unencrypted @@ -0,0 +1,30 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdz +c2gtcnNhAAAAAwEAAQAAAQEAootgTLcKjSgPLS2+RT3ZElhktL1CwIyM/+3IqEq0 +0fl/rRHBT8otklV3Ld7DOR50HVZSoV0u9qs0WOdxcjEJlJACDClmxZmFr0BQ/E2y +V5xzuMZj3Mj+fL26jTmM3ueRHZ0tU5ubSFvINIFyDGG70F7VgkpBA8zsviineMSU +t1iIPi/6feL6h7QAFUk6JdQJpPTs9Nb2+DAQ9lMS2614cxLXkfUIXA4NvHMfZGdU +dq1mBJIAWZ4PPJ6naUcu0lVYjuVEAOE4UoHxr6YlW4yyAF/I1YXBFpcHG7P0egvg +neTPli5Wzum0XDsOPivqr6z2E5k7nzyGXUaP5MjRfDDVLwAAA9D6lTpR+pU6UQAA +AAdzc2gtcnNhAAABAQCii2BMtwqNKA8tLb5FPdkSWGS0vULAjIz/7cioSrTR+X+t +EcFPyi2SVXct3sM5HnQdVlKhXS72qzRY53FyMQmUkAIMKWbFmYWvQFD8TbJXnHO4 +xmPcyP58vbqNOYze55EdnS1Tm5tIW8g0gXIMYbvQXtWCSkEDzOy+KKd4xJS3WIg+ +L/p94vqHtAAVSTol1Amk9Oz01vb4MBD2UxLbrXhzEteR9QhcDg28cx9kZ1R2rWYE +kgBZng88nqdpRy7SVViO5UQA4ThSgfGvpiVbjLIAX8jVhcEWlwcbs/R6C+Cd5M+W +LlbO6bRcOw4+K+qvrPYTmTufPIZdRo/kyNF8MNUvAAAAAwEAAQAAAQB6YVPVDq9s +DfA3RMyQF3vbOyA/kIu0q13xx1cflnfD7AT8CnUwnPloxt5fc+wqkko8WGUIRz93 +yvkzwrYAkvkymKZh/734IpmrlFIlVF5lZk8enIhNkCtDQho2AFGW9mSlFlUtMOhe +N3RqS9fRiLg+r1gzq7J9qQnKNpO48tFBpLkIqr8nZOVhEn8IASrQYBUoocClNrv6 +Pdl8ni5jqnZ/0K0nq4+41Ag1VMI4LUcRCucid8ci1HKdOmGXkvClbzuFMWv3UC0k +qDgzg/gOIgj75I7B34FYVx47UGZ6jmC7iRkHd6RiCHYkmsDSjRQHR6eRbtLPdl9w +TlG2NrwkbSlhAAAAgQCapfJLqew9aK8PKfe3FwiC9sb0itCAXPXHhD+pQ6Tl9UMZ +hmnG2g9qbowCprz3/kyix+nWL/Kx7eKAZYH2MBz6cxfqs2A+BSuxvX/hsnvF96BP +u1I47rGrd0NC78DTY2NDO4Ccirx6uN+AoCl4cC+KC00Kykww6TTEBrQsdQTk5QAA +AIEA7JwbIIMwDiQUt3EY/VU0SYvg67aOiyOYEWplSWCGdT58jnfS1H95kGVw+qXR +eSQ0VNv6LBz7XDRpfQlNXDNJRnDZuHBbk+T9ZwnynRLWuzK7VqZBPJoNoyLFSMW2 +DBhLVKIrg0MsBAnRBMDUlVDlzs2LoNLEra3dj8Zb9vMdlbEAAACBAK/db27GfXXg +OikZkIqWiFgBArtj0T4iFc7BLUJUeFtl0RP9LLjfvaxSdA1cmVYzzkgmuc2iZLF0 +37zuPkDrfYVRiw8rSihT3D+WDt3/Tt013WCuxVQOQSW+Qtw6yZpM92DKncbvYsUy +5DNklW1+TYxyn2ltM7SaZjmF8UeoTnDfAAAAEHJzYS1rZXktMjAyNDExMTgBAgME +BQYHCAkK +-----END OPENSSH PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/rsa_putty_pkcs1_unencrypted b/crates/bitwarden-ssh/resources/import/rsa_putty_pkcs1_unencrypted new file mode 100644 index 00000000..e5bedfbd --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/rsa_putty_pkcs1_unencrypted @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAootgTLcKjSgPLS2+RT3ZElhktL1CwIyM/+3IqEq00fl/rRHB +T8otklV3Ld7DOR50HVZSoV0u9qs0WOdxcjEJlJACDClmxZmFr0BQ/E2yV5xzuMZj +3Mj+fL26jTmM3ueRHZ0tU5ubSFvINIFyDGG70F7VgkpBA8zsviineMSUt1iIPi/6 +feL6h7QAFUk6JdQJpPTs9Nb2+DAQ9lMS2614cxLXkfUIXA4NvHMfZGdUdq1mBJIA +WZ4PPJ6naUcu0lVYjuVEAOE4UoHxr6YlW4yyAF/I1YXBFpcHG7P0egvgneTPli5W +zum0XDsOPivqr6z2E5k7nzyGXUaP5MjRfDDVLwIDAQABAoIBAHphU9UOr2wN8DdE +zJAXe9s7ID+Qi7SrXfHHVx+Wd8PsBPwKdTCc+WjG3l9z7CqSSjxYZQhHP3fK+TPC +tgCS+TKYpmH/vfgimauUUiVUXmVmTx6ciE2QK0NCGjYAUZb2ZKUWVS0w6F43dGpL +19GIuD6vWDOrsn2pCco2k7jy0UGkuQiqvydk5WESfwgBKtBgFSihwKU2u/o92Xye +LmOqdn/QrSerj7jUCDVUwjgtRxEK5yJ3xyLUcp06YZeS8KVvO4Uxa/dQLSSoODOD ++A4iCPvkjsHfgVhXHjtQZnqOYLuJGQd3pGIIdiSawNKNFAdHp5Fu0s92X3BOUbY2 +vCRtKWECgYEA7JwbIIMwDiQUt3EY/VU0SYvg67aOiyOYEWplSWCGdT58jnfS1H95 +kGVw+qXReSQ0VNv6LBz7XDRpfQlNXDNJRnDZuHBbk+T9ZwnynRLWuzK7VqZBPJoN +oyLFSMW2DBhLVKIrg0MsBAnRBMDUlVDlzs2LoNLEra3dj8Zb9vMdlbECgYEAr91v +bsZ9deA6KRmQipaIWAECu2PRPiIVzsEtQlR4W2XRE/0suN+9rFJ0DVyZVjPOSCa5 +zaJksXTfvO4+QOt9hVGLDytKKFPcP5YO3f9O3TXdYK7FVA5BJb5C3DrJmkz3YMqd +xu9ixTLkM2SVbX5NjHKfaW0ztJpmOYXxR6hOcN8CgYASLZAb+Fg5zeXVjhfYZrJk +sB1wno7m+64UMHNlpsfNvCY/n88Pyldhk5mReCnWv8RRfLEEsJlTJSexloReAAay +JbtkYyV2AFLDls0P6kGbEjO4XX+Hk2JW1TYI+D+bQEaRUwA6zm9URBjN3661Zgix +0bLXgTnhCgmKoTexik4MkQKBgEZR14XGzlG81+SpOTeBG4F83ffJ4NfkTy395jf4 +iKubGa/Rcvl1VWU7DvZsyU9Dpb8J5Q+JWJPwdKoZ5UCWKPmO8nidSai4Z3/xY352 +4LTpHdzT5UlH7drGqftfck9FaUEFo3LxM2BAiijWlj1S3HVFO+Ku7JbRigCEQ0bw +0HSnAoGBAJql8kup7D1orw8p97cXCIL2xvSK0IBc9ceEP6lDpOX1QxmGacbaD2pu +jAKmvPf+TKLH6dYv8rHt4oBlgfYwHPpzF+qzYD4FK7G9f+Gye8X3oE+7Ujjusat3 +Q0LvwNNjY0M7gJyKvHq434CgKXhwL4oLTQrKTDDpNMQGtCx1BOTl +-----END RSA PRIVATE KEY----- diff --git a/crates/bitwarden-ssh/resources/import/wrong_label b/crates/bitwarden-ssh/resources/import/wrong_label new file mode 100644 index 00000000..93833b27 --- /dev/null +++ b/crates/bitwarden-ssh/resources/import/wrong_label @@ -0,0 +1,8 @@ +-----BEGIN WRONG LABEL KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRQzzQ8nQEouF1FMSHkPx1nejNCzF7g +Yb8MHXLdBFM0uJkWs0vzgLJkttts2eDv3SHJqIH6qHpkLtEvgMXE5WcaAAAAoOO1BebjtQ +XmAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDPNDydASi4XUUx +IeQ/HWd6M0LMXuBhvwwdct0EUzS4mRazS/OAsmS222zZ4O/dIcmogfqoemQu0S+AxcTlZx +oAAAAhAKnIXk6H0Hs3HblklaZ6UmEjjdE/0t7EdYixpMmtpJ4eAAAAB3Rlc3RrZXk= +-----END WRONG LABEL KEY----- diff --git a/crates/bitwarden-ssh/src/error.rs b/crates/bitwarden-ssh/src/error.rs index f4c0409a..62883f54 100644 --- a/crates/bitwarden-ssh/src/error.rs +++ b/crates/bitwarden-ssh/src/error.rs @@ -6,6 +6,26 @@ use thiserror::Error; pub enum KeyGenerationError { #[error("Failed to generate key: {0}")] KeyGenerationError(ssh_key::Error), - #[error("Failed to convert key: {0}")] - KeyConversionError(ssh_key::Error), + #[error("Failed to convert key")] + KeyConversionError, +} + +#[bitwarden_error(flat)] +#[derive(Error, Debug, PartialEq)] +pub enum SshKeyImportError { + #[error("Failed to parse key")] + ParsingError, + #[error("Password required")] + PasswordRequired, + #[error("Wrong password")] + WrongPassword, + #[error("Unsupported key type")] + UnsupportedKeyType, +} + +#[bitwarden_error(flat)] +#[derive(Error, Debug, PartialEq)] +pub enum SshKeyExportError { + #[error("Failed to convert key")] + KeyConversionError, } diff --git a/crates/bitwarden-ssh/src/generator.rs b/crates/bitwarden-ssh/src/generator.rs new file mode 100644 index 00000000..51b8f9f7 --- /dev/null +++ b/crates/bitwarden-ssh/src/generator.rs @@ -0,0 +1,87 @@ +use serde::{Deserialize, Serialize}; +use ssh_key::{rand_core::CryptoRngCore, Algorithm}; +#[cfg(feature = "wasm")] +use tsify_next::Tsify; + +use crate::{error, error::KeyGenerationError, SshKey}; + +#[derive(Serialize, Deserialize)] +#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] +pub enum KeyAlgorithm { + Ed25519, + Rsa3072, + Rsa4096, +} + +/** + * Generate a new SSH key pair, for the provided key algorithm, returning + * an [SshKey] struct containing the private key, public key, and key fingerprint, + * with the private key in OpenSSH format. + */ +pub fn generate_sshkey(key_algorithm: KeyAlgorithm) -> Result { + let rng = rand::thread_rng(); + generate_sshkey_internal(key_algorithm, rng) +} + +fn generate_sshkey_internal( + key_algorithm: KeyAlgorithm, + mut rng: impl CryptoRngCore, +) -> Result { + let private_key = match key_algorithm { + KeyAlgorithm::Ed25519 => ssh_key::PrivateKey::random(&mut rng, Algorithm::Ed25519) + .map_err(KeyGenerationError::KeyGenerationError), + KeyAlgorithm::Rsa3072 => create_rsa_key(&mut rng, 3072), + KeyAlgorithm::Rsa4096 => create_rsa_key(&mut rng, 4096), + }?; + + private_key + .try_into() + .map_err(|_| KeyGenerationError::KeyConversionError) +} + +fn create_rsa_key( + mut rng: impl CryptoRngCore, + bits: usize, +) -> Result { + let rsa_keypair = ssh_key::private::RsaKeypair::random(&mut rng, bits) + .map_err(KeyGenerationError::KeyGenerationError)?; + let private_key = + ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(rsa_keypair), "") + .map_err(KeyGenerationError::KeyGenerationError)?; + Ok(private_key) +} + +#[cfg(test)] +mod tests { + use rand::SeedableRng; + + use super::KeyAlgorithm; + use crate::generator::generate_sshkey_internal; + + #[test] + fn generate_ssh_key_ed25519() { + let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); + let key_algorithm = KeyAlgorithm::Ed25519; + let result = generate_sshkey_internal(key_algorithm, rng); + let target = include_str!("../resources/generator/ed25519_key").replace("\r\n", "\n"); + assert_eq!(result.unwrap().private_key, target); + } + + #[test] + fn generate_ssh_key_rsa3072() { + let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); + let key_algorithm = KeyAlgorithm::Rsa3072; + let result = generate_sshkey_internal(key_algorithm, rng); + let target = include_str!("../resources/generator/rsa3072_key").replace("\r\n", "\n"); + assert_eq!(result.unwrap().private_key, target); + } + + #[test] + fn generate_ssh_key_rsa4096() { + let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); + let key_algorithm = KeyAlgorithm::Rsa4096; + let result = generate_sshkey_internal(key_algorithm, rng); + let target = include_str!("../resources/generator/rsa4096_key").replace("\r\n", "\n"); + assert_eq!(result.unwrap().private_key, target); + } +} diff --git a/crates/bitwarden-ssh/src/import.rs b/crates/bitwarden-ssh/src/import.rs new file mode 100644 index 00000000..30e92de0 --- /dev/null +++ b/crates/bitwarden-ssh/src/import.rs @@ -0,0 +1,227 @@ +use ed25519; +use pem_rfc7468::PemLabel; +use pkcs8::{der::Decode, pkcs5, DecodePrivateKey, PrivateKeyInfo, SecretDocument}; +use ssh_key::private::{Ed25519Keypair, RsaKeypair}; + +use crate::{error::SshKeyImportError, SshKey}; + +/// Import a PKCS8 or OpenSSH encoded private key, and returns a decoded [SshKey], +/// with the public key and fingerprint, and the private key in OpenSSH format. +/// A password can be provided for encrypted keys. +/// # Returns +/// - [SshKeyImportError::PasswordRequired] if the key is encrypted and no password is provided +/// - [SshKeyImportError::WrongPassword] if the password provided is incorrect +/// - [SshKeyImportError::UnsupportedKeyType] if the key type is not supported +/// - [SshKeyImportError::ParsingError] if the key is otherwise malformed and cannot be parsed +pub fn import_key( + encoded_key: String, + password: Option, +) -> Result { + let label = pem_rfc7468::decode_label(encoded_key.as_bytes()) + .map_err(|_| SshKeyImportError::ParsingError)?; + + match label { + pkcs8::PrivateKeyInfo::PEM_LABEL => import_pkcs8_key(encoded_key, None), + pkcs8::EncryptedPrivateKeyInfo::PEM_LABEL => import_pkcs8_key( + encoded_key, + Some(password.ok_or(SshKeyImportError::PasswordRequired)?), + ), + ssh_key::PrivateKey::PEM_LABEL => import_openssh_key(encoded_key, password), + _ => Err(SshKeyImportError::UnsupportedKeyType), + } +} + +fn import_pkcs8_key( + encoded_key: String, + password: Option, +) -> Result { + let doc = if let Some(password) = password { + SecretDocument::from_pkcs8_encrypted_pem(&encoded_key, password.as_bytes()).map_err( + |err| match err { + pkcs8::Error::EncryptedPrivateKey(pkcs5::Error::DecryptFailed) => { + SshKeyImportError::WrongPassword + } + _ => SshKeyImportError::ParsingError, + }, + )? + } else { + SecretDocument::from_pkcs8_pem(&encoded_key).map_err(|_| SshKeyImportError::ParsingError)? + }; + + let private_key_info = + PrivateKeyInfo::from_der(doc.as_bytes()).map_err(|_| SshKeyImportError::ParsingError)?; + + let private_key = match private_key_info.algorithm.oid { + ed25519::pkcs8::ALGORITHM_OID => { + let private_key: ed25519::KeypairBytes = private_key_info + .try_into() + .map_err(|_| SshKeyImportError::ParsingError)?; + + ssh_key::private::PrivateKey::from(Ed25519Keypair::from(&private_key.secret_key.into())) + } + rsa::pkcs1::ALGORITHM_OID => { + let private_key: rsa::RsaPrivateKey = private_key_info + .try_into() + .map_err(|_| SshKeyImportError::ParsingError)?; + + ssh_key::private::PrivateKey::from( + RsaKeypair::try_from(private_key).map_err(|_| SshKeyImportError::ParsingError)?, + ) + } + _ => return Err(SshKeyImportError::UnsupportedKeyType), + }; + + private_key + .try_into() + .map_err(|_| SshKeyImportError::ParsingError) +} + +fn import_openssh_key( + encoded_key: String, + password: Option, +) -> Result { + let private_key = + ssh_key::private::PrivateKey::from_openssh(&encoded_key).map_err(|err| match err { + ssh_key::Error::AlgorithmUnknown | ssh_key::Error::AlgorithmUnsupported { .. } => { + SshKeyImportError::UnsupportedKeyType + } + _ => SshKeyImportError::ParsingError, + })?; + + let private_key = if private_key.is_encrypted() { + let password = password.ok_or(SshKeyImportError::PasswordRequired)?; + private_key + .decrypt(password.as_bytes()) + .map_err(|_| SshKeyImportError::WrongPassword)? + } else { + private_key + }; + + private_key + .try_into() + .map_err(|_| SshKeyImportError::ParsingError) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn import_key_ed25519_openssh_unencrypted() { + let private_key = include_str!("../resources/import/ed25519_openssh_unencrypted"); + let public_key = include_str!("../resources/import/ed25519_openssh_unencrypted.pub").trim(); + let result = import_key(private_key.to_string(), Some("".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_ed25519_openssh_encrypted() { + let private_key = include_str!("../resources/import/ed25519_openssh_encrypted"); + let public_key = include_str!("../resources/import/ed25519_openssh_encrypted.pub").trim(); + let result = import_key(private_key.to_string(), Some("password".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_rsa_openssh_unencrypted() { + let private_key = include_str!("../resources/import/rsa_openssh_unencrypted"); + let public_key = include_str!("../resources/import/rsa_openssh_unencrypted.pub").trim(); + let result = import_key(private_key.to_string(), Some("".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_rsa_openssh_encrypted() { + let private_key = include_str!("../resources/import/rsa_openssh_encrypted"); + let public_key = include_str!("../resources/import/rsa_openssh_encrypted.pub").trim(); + let result = import_key(private_key.to_string(), Some("password".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_ed25519_pkcs8_unencrypted() { + let private_key = include_str!("../resources/import/ed25519_pkcs8_unencrypted"); + let public_key = include_str!("../resources/import/ed25519_pkcs8_unencrypted.pub") + .replace("testkey", ""); + let public_key = public_key.trim(); + let result = import_key(private_key.to_string(), Some("".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_rsa_pkcs8_unencrypted() { + let private_key = include_str!("../resources/import/rsa_pkcs8_unencrypted"); + // for whatever reason pkcs8 + rsa does not include the comment in the public key + let public_key = + include_str!("../resources/import/rsa_pkcs8_unencrypted.pub").replace("testkey", ""); + let public_key = public_key.trim(); + let result = import_key(private_key.to_string(), Some("".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_rsa_pkcs8_encrypted() { + let private_key = include_str!("../resources/import/rsa_pkcs8_encrypted"); + let public_key = + include_str!("../resources/import/rsa_pkcs8_encrypted.pub").replace("testkey", ""); + let public_key = public_key.trim(); + let result = import_key(private_key.to_string(), Some("password".to_string())).unwrap(); + assert_eq!(result.public_key, public_key); + } + + #[test] + fn import_key_ed25519_openssh_encrypted_wrong_password() { + let private_key = include_str!("../resources/import/ed25519_openssh_encrypted"); + let result = import_key(private_key.to_string(), Some("wrongpassword".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::WrongPassword); + } + + #[test] + fn import_non_key_error() { + let result = import_key("not a key".to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::ParsingError); + } + + #[test] + fn import_wrong_label_error() { + let private_key = include_str!("../resources/import/wrong_label"); + let result = import_key(private_key.to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::UnsupportedKeyType); + } + + #[test] + fn import_ecdsa_error() { + let private_key = include_str!("../resources/import/ecdsa_openssh_unencrypted"); + let result = import_key(private_key.to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::UnsupportedKeyType); + } + + // Putty-exported keys should be supported, but are not due to a parser incompatibility. + // Should this test start failing, please change it to expect a correct key, and + // make sure the documentation support for putty-exported keys this is updated. + // https://bitwarden.atlassian.net/browse/PM-14989 + #[test] + fn import_key_ed25519_putty() { + let private_key = include_str!("../resources/import/ed25519_putty_openssh_unencrypted"); + let result = import_key(private_key.to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::ParsingError); + } + + // Putty-exported keys should be supported, but are not due to a parser incompatibility. + // Should this test start failing, please change it to expect a correct key, and + // make sure the documentation support for putty-exported keys this is updated. + // https://bitwarden.atlassian.net/browse/PM-14989 + #[test] + fn import_key_rsa_openssh_putty() { + let private_key = include_str!("../resources/import/rsa_putty_openssh_unencrypted"); + let result = import_key(private_key.to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::ParsingError); + } + + #[test] + fn import_key_rsa_pkcs8_putty() { + let private_key = include_str!("../resources/import/rsa_putty_pkcs1_unencrypted"); + let result = import_key(private_key.to_string(), Some("".to_string())); + assert_eq!(result.unwrap_err(), SshKeyImportError::UnsupportedKeyType); + } +} diff --git a/crates/bitwarden-ssh/src/lib.rs b/crates/bitwarden-ssh/src/lib.rs index 7736ee33..1a972efe 100644 --- a/crates/bitwarden-ssh/src/lib.rs +++ b/crates/bitwarden-ssh/src/lib.rs @@ -1,100 +1,35 @@ -use error::KeyGenerationError; -use ssh_key::{rand_core::CryptoRngCore, Algorithm, HashAlg, LineEnding}; - pub mod error; +pub mod generator; +pub mod import; +use error::SshKeyExportError; +use pkcs8::LineEnding; use serde::{Deserialize, Serialize}; +use ssh_key::{HashAlg, PrivateKey}; #[cfg(feature = "wasm")] use tsify_next::Tsify; -#[derive(Serialize, Deserialize)] -#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -pub enum KeyAlgorithm { - Ed25519, - Rsa3072, - Rsa4096, -} - -pub fn generate_sshkey( - key_algorithm: KeyAlgorithm, -) -> Result { - let rng = rand::thread_rng(); - generate_sshkey_internal(key_algorithm, rng) -} - -fn generate_sshkey_internal( - key_algorithm: KeyAlgorithm, - mut rng: impl CryptoRngCore, -) -> Result { - let key = match key_algorithm { - KeyAlgorithm::Ed25519 => ssh_key::PrivateKey::random(&mut rng, Algorithm::Ed25519), - KeyAlgorithm::Rsa3072 | KeyAlgorithm::Rsa4096 => { - let bits = match key_algorithm { - KeyAlgorithm::Rsa3072 => 3072, - KeyAlgorithm::Rsa4096 => 4096, - _ => unreachable!(), - }; - - let rsa_keypair = ssh_key::private::RsaKeypair::random(&mut rng, bits) - .map_err(KeyGenerationError::KeyGenerationError)?; - - let private_key = - ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(rsa_keypair), "") - .map_err(KeyGenerationError::KeyGenerationError)?; - Ok(private_key) - } - } - .map_err(KeyGenerationError::KeyGenerationError)?; - - let private_key_openssh = key - .to_openssh(LineEnding::LF) - .map_err(KeyGenerationError::KeyConversionError)?; - Ok(GenerateSshKeyResult { - private_key: private_key_openssh.to_string(), - public_key: key.public_key().to_string(), - key_fingerprint: key.fingerprint(HashAlg::Sha256).to_string(), - }) -} - -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -pub struct GenerateSshKeyResult { +pub struct SshKey { + /// The private key in OpenSSH format pub private_key: String, pub public_key: String, pub key_fingerprint: String, } -#[cfg(test)] -mod tests { - use rand::SeedableRng; +impl TryFrom for SshKey { + type Error = SshKeyExportError; - use super::KeyAlgorithm; - use crate::generate_sshkey_internal; - - #[test] - fn generate_ssh_key_ed25519() { - let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); - let key_algorithm = KeyAlgorithm::Ed25519; - let result = generate_sshkey_internal(key_algorithm, rng); - let target = include_str!("../tests/ed25519_key").replace("\r\n", "\n"); - assert_eq!(result.unwrap().private_key, target); - } - - #[test] - fn generate_ssh_key_rsa3072() { - let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); - let key_algorithm = KeyAlgorithm::Rsa3072; - let result = generate_sshkey_internal(key_algorithm, rng); - let target = include_str!("../tests/rsa3072_key").replace("\r\n", "\n"); - assert_eq!(result.unwrap().private_key, target); - } + fn try_from(value: PrivateKey) -> Result { + let private_key_openssh = value + .to_openssh(LineEnding::LF) + .map_err(|_| SshKeyExportError::KeyConversionError)?; - #[test] - fn generate_ssh_key_rsa4096() { - let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]); - let key_algorithm = KeyAlgorithm::Rsa4096; - let result = generate_sshkey_internal(key_algorithm, rng); - let target = include_str!("../tests/rsa4096_key").replace("\r\n", "\n"); - assert_eq!(result.unwrap().private_key, target); + Ok(SshKey { + private_key: private_key_openssh.to_string(), + public_key: value.public_key().to_string(), + key_fingerprint: value.fingerprint(HashAlg::Sha256).to_string(), + }) } } diff --git a/crates/bitwarden-wasm-internal/src/ssh.rs b/crates/bitwarden-wasm-internal/src/ssh.rs index e3ac77bf..1255a573 100644 --- a/crates/bitwarden-wasm-internal/src/ssh.rs +++ b/crates/bitwarden-wasm-internal/src/ssh.rs @@ -1,8 +1,37 @@ use wasm_bindgen::prelude::*; +/// Generate a new SSH key pair +/// +/// # Arguments +/// - `key_algorithm` - The algorithm to use for the key pair +/// +/// # Returns +/// - `Ok(SshKey)` if the key was successfully generated +/// - `Err(KeyGenerationError)` if the key could not be generated #[wasm_bindgen] pub fn generate_ssh_key( - key_algorithm: bitwarden_ssh::KeyAlgorithm, -) -> Result { - bitwarden_ssh::generate_sshkey(key_algorithm) + key_algorithm: bitwarden_ssh::generator::KeyAlgorithm, +) -> Result { + bitwarden_ssh::generator::generate_sshkey(key_algorithm) +} + +/// Convert a PCKS8 or OpenSSH encrypted or unencrypted private key +/// to an OpenSSH private key with public key and fingerprint +/// +/// # Arguments +/// - `imported_key` - The private key to convert +/// - `password` - The password to use for decrypting the key +/// +/// # Returns +/// - `Ok(SshKey)` if the key was successfully coneverted +/// - `Err(PasswordRequired)` if the key is encrypted and no password was provided +/// - `Err(WrongPassword)` if the password provided is incorrect +/// - `Err(ParsingError)` if the key could not be parsed +/// - `Err(UnsupportedKeyType)` if the key type is not supported +#[wasm_bindgen] +pub fn import_ssh_key( + imported_key: &str, + password: Option, +) -> Result { + bitwarden_ssh::import::import_key(imported_key.to_string(), password) }