1
0
mirror of https://github.com/libssh2/libssh2.git synced 2026-10-24 03:48:58 +03:00

src: add support for chacha20-poly130-encrypted OpenSSH key files

Also:
- switch encrypted ECDSA test key to use chacha20-poly1305@openssh.com.
- pem: add debug message on decryption error.

Follow-up to fd07ca411f #2486
Follow-up to 492bc543bb #1426

Closes #2488
This commit is contained in:
Viktor Szakats
2026-08-02 23:48:09 +02:00
parent 10af31ddf1
commit 7abbac4e7f
8 changed files with 49 additions and 29 deletions

View File

@@ -78,6 +78,7 @@ struct crypt_ctx {
int encrypt;
SSH2_CIPHER_T(algo);
ssh2_cipher_ctx h;
int privkeyfile; /* for chachapoly decrypt */
struct chachapoly_ctx chachapoly_ctx;
};
@@ -87,13 +88,14 @@ static int crypt_init(LIBSSH2_SESSION *session,
const struct crypt_method *method,
unsigned char *iv, int *free_iv,
unsigned char *secret, int *free_secret,
int encrypt, void **abstract)
int encrypt, int privkeyfile, void **abstract)
{
struct crypt_ctx *ctx = SSH2_ALLOC(session, sizeof(struct crypt_ctx));
if(!ctx)
return LIBSSH2_ERROR_ALLOC;
ctx->encrypt = encrypt;
ctx->privkeyfile = privkeyfile;
ctx->algo = method->algo;
if(ssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) {
SSH2_FREE(session, ctx);
@@ -310,12 +312,12 @@ static int crypt_init_arcfour128(LIBSSH2_SESSION *session,
const struct crypt_method *method,
unsigned char *iv, int *free_iv,
unsigned char *secret, int *free_secret,
int encrypt, void **abstract)
int encrypt, int privkeyfile, void **abstract)
{
int rc;
rc = crypt_init(session, method, iv, free_iv, secret, free_secret,
encrypt, abstract);
encrypt, privkeyfile, abstract);
if(rc == 0) {
struct crypt_ctx *cctx = *(struct crypt_ctx **)abstract;
unsigned char block[8];
@@ -383,7 +385,8 @@ static int crypt_init_chacha20_poly(LIBSSH2_SESSION *session,
const struct crypt_method *method,
unsigned char *iv, int *free_iv,
unsigned char *secret, int *free_secret,
int encrypt, void **abstract)
int encrypt, int privkeyfile,
void **abstract)
{
struct crypt_ctx *ctx = SSH2_ALLOC(session, sizeof(struct crypt_ctx));
@@ -393,6 +396,7 @@ static int crypt_init_chacha20_poly(LIBSSH2_SESSION *session,
return LIBSSH2_ERROR_ALLOC;
ctx->encrypt = encrypt;
ctx->privkeyfile = privkeyfile;
ctx->algo = method->algo;
if(chachapoly_init(&ctx->chachapoly_ctx, secret, method->secret_len)) {
@@ -430,7 +434,7 @@ static int crypt_encrypt_chacha20_poly_buffer(LIBSSH2_SESSION *session,
ret = chachapoly_crypt(&ctx->chachapoly_ctx, seqno, buf, buf,
buf_len - 4, 4, ctx->encrypt);
}
else {
else if(!ctx->privkeyfile) {
/* buf is full packet including size and auth tag but buf_len
does not include size */
if(buf_len < 4)
@@ -444,6 +448,10 @@ static int crypt_encrypt_chacha20_poly_buffer(LIBSSH2_SESSION *session,
if(ret == 0)
memmove(buf, buf + 4, buf_len - 4);
}
else
/* for private key decryption */
ret = chachapoly_crypt(&ctx->chachapoly_ctx, seqno, buf, buf,
buf_len, 0, ctx->encrypt);
}
return ret == 0 ? 0 : 1; /* success: 0, fail: 1 */

View File

@@ -237,7 +237,7 @@ static int kex_finish(LIBSSH2_SESSION *session,
}
if(session->local.crypt->init(session, session->local.crypt, iv,
&free_iv, secret, &free_secret, 1,
&free_iv, secret, &free_secret, 1, 0,
&session->local.crypt_abstract)) {
SSH2_FREE(session, iv);
SSH2_FREE(session, secret);
@@ -281,7 +281,7 @@ static int kex_finish(LIBSSH2_SESSION *session,
"key exchange");
}
if(session->remote.crypt->init(session, session->remote.crypt, iv,
&free_iv, secret, &free_secret, 0,
&free_iv, secret, &free_secret, 0, 0,
&session->remote.crypt_abstract)) {
SSH2_FREE(session, iv);
SSH2_FREE(session, secret);

View File

@@ -1055,7 +1055,7 @@ struct crypt_method {
int (*init)(LIBSSH2_SESSION *session,
const struct crypt_method *method, unsigned char *iv,
int *free_iv, unsigned char *secret, int *free_secret,
int encrypt, void **abstract);
int encrypt, int privkeyfile, void **abstract);
int (*get_len)(LIBSSH2_SESSION *session, unsigned int seqno,
unsigned char *data, size_t data_size, unsigned int *len,
void **abstract);

View File

@@ -297,8 +297,8 @@ int ssh2_pem_parse(LIBSSH2_SESSION *session,
}
/* Initialize the decryption */
if(method->init(session, method, iv, &free_iv, secret, &free_secret, 0,
&abstract)) {
if(method->init(session, method, iv, &free_iv, secret,
&free_secret, 0, 1, &abstract)) {
ssh2_explicit_zero(secret, sizeof(secret));
goto out;
}
@@ -579,19 +579,22 @@ static int pem_parse_data_openssh(LIBSSH2_SESSION *session,
goto out;
}
iv_part = SSH2_CALLOC(session, ivlen);
if(!iv_part) {
ret = ssh2_err(session, LIBSSH2_ERROR_PROTO,
"Could not alloc iv part");
goto out;
if(ivlen) {
iv_part = SSH2_CALLOC(session, ivlen);
if(!iv_part) {
ret = ssh2_err(session, LIBSSH2_ERROR_PROTO,
"Could not alloc IV part");
goto out;
}
}
memcpy(key_part, key, keylen);
memcpy(iv_part, key + keylen, ivlen);
if(iv_part)
memcpy(iv_part, key + keylen, ivlen);
/* Initialize the decryption */
if(method->init(session, method, iv_part, &free_iv, key_part,
&free_secret, 0, &abstract)) {
&free_secret, 0, 1, &abstract)) {
ret = LIBSSH2_ERROR_DECRYPT;
goto out;
}
@@ -604,9 +607,16 @@ static int pem_parse_data_openssh(LIBSSH2_SESSION *session,
}
if(method->flags & SSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET) {
if(!ssh2_check_length(&decoded, method->auth_len)) {
ret = ssh2_err(session, LIBSSH2_ERROR_PROTO,
"OpenSSH key full packet auth tag missing");
method->dtor(session, &abstract);
goto out;
}
if(method->crypt(session, 0, decrypted.data,
decrypted.len, &abstract, MIDDLE_BLOCK)) {
ret = LIBSSH2_ERROR_DECRYPT;
ret = ssh2_err(session, LIBSSH2_ERROR_DECRYPT,
"OpenSSH key full packet decrypt failed");
method->dtor(session, &abstract);
goto out;
}
@@ -622,7 +632,8 @@ static int pem_parse_data_openssh(LIBSSH2_SESSION *session,
*/
if(method->crypt(session, 0, decrypted.data + len_decrypted,
blocksize, &abstract, MIDDLE_BLOCK)) {
ret = LIBSSH2_ERROR_DECRYPT;
ret = ssh2_err(session, LIBSSH2_ERROR_DECRYPT,
"OpenSSH key decrypt failed");
method->dtor(session, &abstract);
goto out;
}

View File

@@ -45,7 +45,7 @@ ssh-keygen -t rsa -b 4096 -N '' -m PEM -C 'id_rsa_sha2_512_sign
ssh-keygen -t rsa-sha2-512 -I "${id}" -n "${pr}" -s 'openssh_server/ca_rsa' 'keys/id_rsa_sha2_512_signed.pub'
ssh-keygen -t ecdsa -b 384 -N '' -m RFC4716 -C '' -f 'keys/id_ecdsa' # empty comment
ssh-keygen -t ecdsa -b 256 -N "${pw}" -m RFC4716 -C 'id_ecdsa_encrypted' -f 'keys/id_ecdsa_encrypted' -Z aes256-ctr
ssh-keygen -t ecdsa -b 256 -N "${pw}" -m RFC4716 -C 'id_ecdsa_encrypted' -f 'keys/id_ecdsa_encrypted' -Z chacha20-poly1305@openssh.com
ssh-keygen -t ecdsa -b 384 -N '' -m RFC4716 -C 'id_ecdsa_signed' -f 'keys/id_ecdsa_signed'
ssh-keygen -I "${id}" -n "${pr}" -s 'openssh_server/ca_ecdsa' 'keys/id_ecdsa_signed.pub'

View File

@@ -1,9 +1,10 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCexYpKz/
UV1UYiV/3FlloPAAAAGAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz
dHAyNTYAAABBBOq2ZOQo5aWIsS6eOUHMCtQdg5qNZnIiwLGN76QgJVySA5BZ2gCjPEpnzG
kitAXeKA/CA6E9oUrTXLfk5Pt49CgAAACwG6cRJOtaOQ8R6+mruqqbU/Pu6OqX87ulBepH
TcKpai4ou1c9WOD6FPbNyPUhrcBYo6qxrYHWMUqxfYnRKJp2UmTLNKjOSU7nY7Z/szTbov
XQnAT3Fe3ygcsmOG+HYUtFJGs0vAPZj2l/oiX84hBIX41Zw5TZIHqiRdkHBXWutTNFJ3xk
UKr7MT2bFvCcq8DCFW8clUFGsuiHdH96K4MMgT0YanviNjKCY3cEsbnxb+g=
b3BlbnNzaC1rZXktdjEAAAAAHWNoYWNoYTIwLXBvbHkxMzA1QG9wZW5zc2guY29tAAAABm
JjcnlwdAAAABgAAAAQwXm70kEh9KMQqGC2nC3ImwAAABgAAAABAAAAaAAAABNlY2RzYS1z
aGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQS6NmAMYyM3OJyytjoRAAfOojCNl7JkEG
hz5dNis8QqVkfNElFMTlclxH3//pcJdh4BPu1KPq81g6zLK2M1uDRiAAAAsAULX7/Io52P
CsWJIhzX49Hv37En5dQX+zA+3wqwW9YyN9//Ss6mD7qgLEuRVUY9HijwF3S1yTgah7GBN0
YZfkwlOPga1/lgAPZH/LNhtdUfl6PqX4mshMLApdmI9vzLkMCNyHzEqVwjBPWU3ULK3uQl
mMX2nPJwiMUAxvyqOtVj9b9ZW7CP2pNrkWW2anb36P3YqLc2vevdJdo1LtY+h8JE0ohoiC
lvbA86fzMeLFN/t9+bbvszc/a2ry57D3WQTQ==
-----END OPENSSH PRIVATE KEY-----

View File

@@ -1 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOq2ZOQo5aWIsS6eOUHMCtQdg5qNZnIiwLGN76QgJVySA5BZ2gCjPEpnzGkitAXeKA/CA6E9oUrTXLfk5Pt49Cg= id_ecdsa_encrypted
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLo2YAxjIzc4nLK2OhEAB86iMI2XsmQQaHPl02KzxCpWR80SUUxOVyXEff/+lwl2HgE+7Uo+rzWDrMsrYzW4NGI= id_ecdsa_encrypted

View File

@@ -4,6 +4,6 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC92YlGoc4PJy6DzX916JJZhxkvmkWBLGJdWOL7R9B6
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTe1lN2L/yet0Ma1JzXkQf3t1f+pauALec2FsGZy87KRJW1AOxcTTiePjlFwP1yfSK1lWXQ+uf0b61gkKqqR52FDky24HJWuYlfXlEQMn2d/PNDNVDDbO4TXKyNxxUHFJ6qYMNd4kWjOH+6rmYoWKsWV+3mDRbHagbVPEYL8wep8OTqKOqruVLVPzZyYZkBtn4XOFi6UE8WKiSVdK1Am1O5UxvlD95t32eYch6wQ9azgMqja6spe/L5UJgP83QZFknVC3wPZWkjqomVFql0FpaQclENwyY/OZMxr0cT/f7bCL6s4A/1XpbsGmC0xak4/THHbOn+0LdIej2nGV8JFoR
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD+1l4u2KtpvsP2EpioqgHjfirT7IIQOQfCsjZCppmLUcPE1/qCC7krqjzaQ3nuKlVEc/pKJrti2oc0tBDp6zzr8pPkmzdMtpjP84RkJZwu6eVZyZqQrjpmwjw7IWVbGoxkKFpWS2ndxCzhoD/F+TW2NcjpwFN53Ktq0SdHVUXU00RlzUiKNTdzanb8vyF3m+PGwIWPX92E2t/31WxRVXwybTdpXSzyz9IIjxMkrSRVa8XzX0EyUzETuiAeU94AWJX5Gu/pFJT6E7VEMQudeN4iI75WYGxrYU1HCOTnsYk/9LCdM7O6ntJIlzul3CwXsD6+B35hu5AJb91RNbHYWUz key_rsa_aes256gcm
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOiyJJwf+hFJrOkik9IQVxjhhVnI1vKl4BGLj03erLvw4TXXwoZzlPx6J936fU9JvvPw+d8PA4viMEfFbmdbHREKhwi3u1vZrfj0mB8SXw96AcZwX8PAL556wgom+/Qx+Q==
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOq2ZOQo5aWIsS6eOUHMCtQdg5qNZnIiwLGN76QgJVySA5BZ2gCjPEpnzGkitAXeKA/CA6E9oUrTXLfk5Pt49Cg= id_ecdsa_encrypted
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLo2YAxjIzc4nLK2OhEAB86iMI2XsmQQaHPl02KzxCpWR80SUUxOVyXEff/+lwl2HgE+7Uo+rzWDrMsrYzW4NGI= id_ecdsa_encrypted
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFEg0II3zbc1yWGhRzIc5KwTRlmx10RQP0sS63+1oMPi
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICHxEyUTOVHXvdMFARedFQ+H9DW/n8Zy3daKKRqnTDMq