Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ed25519 SSH private keys #461

Open
CodeFetch opened this issue Jun 19, 2021 · 9 comments
Open

Support ed25519 SSH private keys #461

CodeFetch opened this issue Jun 19, 2021 · 9 comments

Comments

@CodeFetch
Copy link

CodeFetch commented Jun 19, 2021

It would be great if one could define ed25519 SSH private keys for pubkey authentication without ssh-agent.
This is especially interesting for embedded distributions like OpenWrt or Alpine Linux where busybox/dropbear is used as default SSH client/server and ssh-agent is not available.

@CodeFetch CodeFetch changed the title Support SSH private keys Support ed25519 SSH private keys Jun 19, 2021
@tobiasbrunner
Copy link
Member

What version did you use? What plugins were loaded? What format did the keys have?

@CodeFetch
Copy link
Author

CodeFetch commented Jun 21, 2021

I use 5.2 5.6.2 and the private key is in ed25519 OpenSSH format.

loaded plugins: charon pkcs11 aes des rc2 sha2 sha3 sha1 md5 mgf1 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem fips-prf gmp gmpdh curve25519 chapoly xcbc hmac ctr attr kernel-netlink resolve socket-default socket-dynamic connmark vici updown xauth-generic

root@OpenWrt:~# swanctl --list-algs
encryption:
  AES_CBC[aes]
  AES_ECB[aes]
  3DES_CBC[des]
  DES_CBC[des]
  DES_ECB[des]
  RC2_CBC[rc2]
  AES_CTR[ctr]
integrity:
  AES_XCBC_96[xcbc]
  HMAC_SHA1_96[hmac]
  HMAC_SHA1_128[hmac]
  HMAC_SHA1_160[hmac]
  HMAC_MD5_96[hmac]
  HMAC_MD5_128[hmac]
  HMAC_SHA2_256_128[hmac]
  HMAC_SHA2_256_256[hmac]
  HMAC_SHA2_384_192[hmac]
  HMAC_SHA2_384_384[hmac]
  HMAC_SHA2_512_256[hmac]
  HMAC_SHA2_512_512[hmac]
aead:
  CHACHA20_POLY1305[chapoly]
hasher:
  HASH_SHA1[sha1]
  HASH_SHA2_224[sha2]
  HASH_SHA2_256[sha2]
  HASH_SHA2_384[sha2]
  HASH_SHA2_512[sha2]
  HASH_SHA3_224[sha3]
  HASH_SHA3_256[sha3]
  HASH_SHA3_384[sha3]
  HASH_SHA3_512[sha3]
  HASH_MD5[md5]
  HASH_IDENTITY[curve25519]
prf:
  PRF_KEYED_SHA1[sha1]
  PRF_FIPS_SHA1_160[fips-prf]
  PRF_AES128_XCBC[xcbc]
  PRF_HMAC_SHA1[hmac]
  PRF_HMAC_MD5[hmac]
  PRF_HMAC_SHA2_256[hmac]
  PRF_HMAC_SHA2_384[hmac]
  PRF_HMAC_SHA2_512[hmac]
xof:
  XOF_SHAKE128[sha3]
  XOF_SHAKE256[sha3]
  XOF_MGF1_SHA1[mgf1]
  XOF_MGF1_SHA224[mgf1]
  XOF_MGF1_SHA256[mgf1]
  XOF_MGF1_SHA384[mgf1]
  XOF_MGF1_SHA512[mgf1]
  XOF_CHACHA20[chapoly]
drbg:
dh:
  MODP_3072[gmp]
  MODP_4096[gmp]
  MODP_6144[gmp]
  MODP_8192[gmp]
  MODP_2048[gmp]
  MODP_2048_224[gmp]
  MODP_2048_256[gmp]
  MODP_1536[gmp]
  MODP_1024[gmp]
  MODP_1024_160[gmp]
  MODP_768[gmp]
  MODP_CUSTOM[gmp]
  CURVE_25519[curve25519]
rng:
  RNG_STRONG[random]
  RNG_TRUE[random]
nonce-gen:
  NONCE_GEN[nonce]

This is the log output:

Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[CFG] vici client 1 requests: load-key
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   file content is not binary ASN.1
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   -----BEGIN OPENSSH PRIVATE KEY-----
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   -----END OPENSSH PRIVATE KEY-----
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] L0 - RSAPrivateKey: ASN1 tag 0x30 expected, but is 0x6f
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] => 114 bytes @ 0x005bc000
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]    0: 6F 70 65 6E 73 73 68 2D 6B 65 79 2D 76 31 00 00  openssh-key-v1..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   16: 00 00 04 6E 6F 6E 65 00 00 00 04 6E 6F 6E 65 00  ...none....none.
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   32: 00 00 00 00 00 00 01 00 00 00 33 00 00 00 0B 73  ..........3....s
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   48: 73 68 2D 65 64 32 35 35 31 39 00 00 00 20 E4 A9  sh-ed25519... ..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   64: A4 07 B7 AF EB 2E 9B C6 74 ED 60 1A 9C 3C B3 D7  ........t.`..<..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   80: 46 5C 09 2C CE 7F 89 5C 8A 09 50 DB D3 B9 00 00  F\.,...\..P.....
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   96: 00 88 00 00 00 00 00 00 00 00 00 00 00 0B 73 73  ..............ss
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]  112: 68 2D                                            h-
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] L0 - encryptedPrivateKeyInfo: ASN1 tag 0x30 expected, but is 0x6f
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] => 114 bytes @ 0x005bc000
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]    0: 6F 70 65 6E 73 73 68 2D 6B 65 79 2D 76 31 00 00  openssh-key-v1..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   16: 00 00 04 6E 6F 6E 65 00 00 00 04 6E 6F 6E 65 00  ...none....none.
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   32: 00 00 00 00 00 00 01 00 00 00 33 00 00 00 0B 73  ..........3....s
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   48: 73 68 2D 65 64 32 35 35 31 39 00 00 00 20 E4 A9  sh-ed25519... ..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   64: A4 07 B7 AF EB 2E 9B C6 74 ED 60 1A 9C 3C B3 D7  ........t.`..<..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   80: 46 5C 09 2C CE 7F 89 5C 8A 09 50 DB D3 B9 00 00  F\.,...\..P.....
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   96: 00 88 00 00 00 00 00 00 00 00 00 00 00 0B 73 73  ..............ss
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]  112: 68 2D                                            h-
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] L0 - privateKeyInfo: ASN1 tag 0x30 expected, but is 0x6f
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN] => 114 bytes @ 0x005bc000
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]    0: 6F 70 65 6E 73 73 68 2D 6B 65 79 2D 76 31 00 00  openssh-key-v1..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   16: 00 00 04 6E 6F 6E 65 00 00 00 04 6E 6F 6E 65 00  ...none....none.
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   32: 00 00 00 00 00 00 01 00 00 00 33 00 00 00 0B 73  ..........3....s
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   48: 73 68 2D 65 64 32 35 35 31 39 00 00 00 20 E4 A9  sh-ed25519... ..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   64: A4 07 B7 AF EB 2E 9B C6 74 ED 60 1A 9C 3C B3 D7  ........t.`..<..
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   80: 46 5C 09 2C CE 7F 89 5C 8A 09 50 DB D3 B9 00 00  F\.,...\..P.....
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]   96: 00 88 00 00 00 00 00 00 00 00 00 00 00 0B 73 73  ..............ss
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[ASN]  112: 68 2D                                            h-
Mon Jun 21 16:39:43 2021 daemon.info ipsec: 06[LIB] building CRED_PRIVATE_KEY - ANY failed, tried 5 builders

And this is the corresponding private key:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
c2gtZWQyNTUxOQAAACDkqaQHt6/rLpvGdO1gGpw8s9dGXAkszn+JXIoJUNvTuQAA
AIgAAAAAAAAAAAAAAAtzc2gtZWQyNTUxOQAAACDkqaQHt6/rLpvGdO1gGpw8s9dG
XAkszn+JXIoJUNvTuQAAAEALtskyIjFfQ1FSyu/6Qhy/L8+zzSjIJ3cajs99tXK8
ReSppAe3r+sum8Z07WAanDyz10ZcCSzOf4lciglQ29O5AAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----

@tobiasbrunner
Copy link
Member

Thanks for the details. Yes, that format is currently not supported. While the OpenSSH public key format is specified in several RFCs (see here for links), the private key format seems only roughly specified here (also linked on the other page). Besides the format itself, the private key is apparently protected by bcrypt (if it's protected by a passphrase), which we currently don't implement either. So I you currently won't get around converting the private key to PKCS#8 if you can't use ssh-agent (apparently ssh-keygen -p -m pem should work for an in-place conversion, but it doesn't with the version of OpenSSH I have here).

@CodeFetch
Copy link
Author

I see... Unfortunately I don't have much time at the moment. Otherwise I'd have a look at it. I've tested dropbear's ed25519 key converted with dropbearconvert with OpenSSH. They seem to do it right. Although their code lacks a few comments and they don't support encrypted keys this seems to be a good starting point:
https://github.com/mkj/dropbear/blob/846d38fe4319c517683ac3df1796b3bc0180be14/keyimport.c#L636

@CodeFetch
Copy link
Author

The private key portion of the base64-decoded key above is:
0bb6 c932 2231 5f43 5152 caef fa42 1cbf 2fcf b3cd 28c8 2777 1a8e cf7d b572 bc45

Adding the correct PER-header:

0000000 302e 0201 0030 0506 032b 6570 0422 0420
0000010 0bb6 c932 2231 5f43 5152 caef fa42 1cbf
0000020 2fcf b3cd 28c8 2777 1a8e cf7d b572 bc45

Resulted in a working key:

root@OpenWrt:~/test# pki --pub --outform sshkey -i dropbear2.key
AAAAC3NzaC1lZDI1NTE5AAAAIOSppAe3r+sum8Z07WAanDyz10ZcCSzOf4lciglQ29O5

root@OpenWrt:~/test# dropbearkey -y -f /etc/dropbear/dropbear_ed25519_host_key 
Public key portion is:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOSppAe3r+sum8Z07WAanDyz10ZcCSzOf4lciglQ29O5 root@OpenWrt
Fingerprint: sha1!! 65:0c:c8:e9:61:95:b7:be:5d:7d:c7:f2:37:e1:a8:ad:8e:56:10:1a

@tobiasbrunner Thank you for your kind help!
I guess you have wondered about this line (and assumed the key I sent was encrypted or something):
https://github.com/mkj/dropbear/blob/a8d6dac2c53f430bb5721f913478bd294d8b52da/ed25519.c#L82

Seems it's just a bug in dropbear... I've tried to convert the resulting key back to dropbear format using dropbearconvert.
It does not even get to that point:

Error: File does not begin with OpenSSH key header
Error reading key from '/etc/dropbear/dropbear_ed25519_host_key'

Counting is hard:
https://github.com/mkj/dropbear/blob/a8d6dac2c53f430bb5721f913478bd294d8b52da/keyimport.c#L402

Even without that bug they have mixed up private and public key here I guess:
https://github.com/mkj/dropbear/blob/a8d6dac2c53f430bb5721f913478bd294d8b52da/ed25519.c#L82

@tobiasbrunner
Copy link
Member

I guess you have wondered about this line (and assumed the key I sent was encrypted or something):

No, I was referring to the documentation of the format that mentions bcrypt (I guess there are other KDF options, but those are not documented, neither are the ciphers, so not sure what would actually get used), not your key, which was not protected (none for both the cipher- and kdfname fields).

Counting is hard:

😂

Even without that bug they have mixed up private and public key here I guess:

The order is correct but they don't seem to skip the public key. Because looking at the encoding, we see that after ssh-ed25519 (the second instance in the private key section) follows the 32-byte public key (00 0000 20e4 a9a4...b9), which is then followed by the 64-byte concatenation of the private key and again the public key (b900 0000 400b b6c9...45e4 a9a4...b9), which is what they are trying to parse. So the private key portion of the format for ed25519 (they say it's the format used by ssh-agent) apparently encodes first the public key and then the private and again the public key. So the public key is actually encoded three times in the complete key structure as it's also encoded in the initial public key section.

@tobiasbrunner
Copy link
Member

I can confirm neither ed25519 keys nor *.p12 files work on the Android app:

Which is completely unrelated because it has nothing to do with OpenSSH's private key form and signature generation on Android is implemented via Android's KeyChain API (and the stuff in java.security, in particular, Signature). So you'd have to ask Google for EdDSA support in both if that's something you want.

@Thermi
Copy link
Contributor

Thermi commented Oct 11, 2021

Aaaand the issue is dead?

@CodeFetch
Copy link
Author

@Thermi I've written a Lua script which takes care of converting it to PER format and I'm quite happy with it. Feel free to close this issue if this feature does not make sense to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants