ed25519: Add support o import OpenSSH container keys
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
2f7886837f
Коммит
3ec3a926e5
@ -27,6 +27,10 @@
|
||||
#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
|
||||
#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----"
|
||||
#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----"
|
||||
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
|
||||
#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
|
||||
/* Magic defined in OpenSSH/PROTOCOL.key */
|
||||
#define OPENSSH_AUTH_MAGIC "openssh-key-v1"
|
||||
|
||||
#define ssh_pki_log(...) \
|
||||
_ssh_pki_log(__FUNCTION__, __VA_ARGS__)
|
||||
@ -105,4 +109,8 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key);
|
||||
ssh_string pki_ed25519_sig_to_blob(ssh_signature sig);
|
||||
int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob);
|
||||
|
||||
/* PKI Container OpenSSH */
|
||||
ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
|
||||
const char *passphrase, ssh_auth_callback auth_fn, void *auth_data);
|
||||
|
||||
#endif /* PKI_PRIV_H_ */
|
||||
|
@ -139,6 +139,7 @@ set(libssh_SRCS
|
||||
packet_crypt.c
|
||||
pcap.c
|
||||
pki.c
|
||||
pki_container_openssh.c
|
||||
pki_ed25519.c
|
||||
poll.c
|
||||
session.c
|
||||
|
33
src/pki.c
33
src/pki.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* known_hosts.c
|
||||
* pki.c
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2010 by Aris Adamantiadis
|
||||
@ -402,6 +402,7 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
|
||||
ssh_key *pkey)
|
||||
{
|
||||
ssh_key key;
|
||||
int cmp;
|
||||
|
||||
if (b64_key == NULL || pkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
@ -414,7 +415,20 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
|
||||
ssh_pki_log("Trying to decode privkey passphrase=%s",
|
||||
passphrase ? "true" : "false");
|
||||
|
||||
key = pki_private_key_from_base64(b64_key, passphrase, auth_fn, auth_data);
|
||||
/* Test for OpenSSH key format first */
|
||||
cmp = strncmp(b64_key, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
|
||||
if (cmp == 0) {
|
||||
key = ssh_pki_openssh_privkey_import(b64_key,
|
||||
passphrase,
|
||||
auth_fn,
|
||||
auth_data);
|
||||
} else {
|
||||
/* fallback on PEM decoder */
|
||||
key = pki_private_key_from_base64(b64_key,
|
||||
passphrase,
|
||||
auth_fn,
|
||||
auth_data);
|
||||
}
|
||||
if (key == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
@ -451,7 +465,6 @@ int ssh_pki_import_privkey_file(const char *filename,
|
||||
ssh_key *pkey) {
|
||||
struct stat sb;
|
||||
char *key_buf;
|
||||
ssh_key key;
|
||||
FILE *file;
|
||||
off_t size;
|
||||
int rc;
|
||||
@ -505,14 +518,14 @@ int ssh_pki_import_privkey_file(const char *filename,
|
||||
}
|
||||
key_buf[size] = 0;
|
||||
|
||||
key = pki_private_key_from_base64(key_buf, passphrase, auth_fn, auth_data);
|
||||
SAFE_FREE(key_buf);
|
||||
if (key == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
rc = ssh_pki_import_privkey_base64(key_buf,
|
||||
passphrase,
|
||||
auth_fn,
|
||||
auth_data,
|
||||
pkey);
|
||||
|
||||
*pkey = key;
|
||||
return SSH_OK;
|
||||
SAFE_FREE(key_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
254
src/pki_container_openssh.c
Обычный файл
254
src/pki_container_openssh.c
Обычный файл
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* pki_container_openssh.c
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013,2014 Aris Adamantiadis <aris@badcode.be>
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* The SSH Library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup libssh_pki
|
||||
* *
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/pki_priv.h"
|
||||
#include "libssh/buffer.h"
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Import a private key from a ssh buffer.
|
||||
*
|
||||
* @param[in] key_blob_buffer The key blob to import as specified in
|
||||
* key.c:key_private_serialize in OpenSSH source
|
||||
* code.
|
||||
*
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
|
||||
ssh_key *pkey)
|
||||
{
|
||||
enum ssh_keytypes_e type;
|
||||
char *type_s = NULL;
|
||||
ssh_key key = NULL;
|
||||
ssh_string pubkey = NULL, privkey = NULL;
|
||||
int rc;
|
||||
|
||||
if (pkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
|
||||
if (rc == SSH_ERROR){
|
||||
ssh_pki_log("Unpack error");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
type = ssh_key_type_from_name(type_s);
|
||||
if (type == SSH_KEYTYPE_UNKNOWN) {
|
||||
ssh_pki_log("Unknown key type found!");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SAFE_FREE(type_s);
|
||||
|
||||
key = ssh_key_new();
|
||||
if (key == NULL) {
|
||||
ssh_pki_log("Out of memory");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
key->type = type;
|
||||
key->type_c = ssh_key_type_to_char(type);
|
||||
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
|
||||
|
||||
switch (type) {
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
rc = ssh_buffer_unpack(key_blob_buffer, "SS", &pubkey, &privkey);
|
||||
if (rc != SSH_OK){
|
||||
ssh_pki_log("Unpack error");
|
||||
goto fail;
|
||||
}
|
||||
if(ssh_string_len(pubkey) != ED25519_PK_LEN ||
|
||||
ssh_string_len(privkey) != ED25519_SK_LEN){
|
||||
ssh_pki_log("Invalid ed25519 key len");
|
||||
goto fail;
|
||||
}
|
||||
key->ed25519_privkey = malloc(ED25519_SK_LEN);
|
||||
key->ed25519_pubkey = malloc(ED25519_PK_LEN);
|
||||
if(key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL){
|
||||
goto fail;
|
||||
}
|
||||
memcpy(key->ed25519_privkey, ssh_string_data(privkey), ED25519_SK_LEN);
|
||||
memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN);
|
||||
memset(ssh_string_data(privkey), 0, ED25519_SK_LEN);
|
||||
SAFE_FREE(privkey);
|
||||
SAFE_FREE(pubkey);
|
||||
break;
|
||||
case SSH_KEYTYPE_DSS:
|
||||
/* p,q,g,pub_key,priv_key */
|
||||
case SSH_KEYTYPE_RSA:
|
||||
/* n,e,d,iqmp,p,q */
|
||||
case SSH_KEYTYPE_RSA1:
|
||||
case SSH_KEYTYPE_ECDSA:
|
||||
/* curve_name, group, privkey */
|
||||
ssh_pki_log("Unsupported private key method %s", key->type_c);
|
||||
goto fail;
|
||||
case SSH_KEYTYPE_UNKNOWN:
|
||||
ssh_pki_log("Unknown private key protocol %s", key->type_c);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*pkey = key;
|
||||
return SSH_OK;
|
||||
fail:
|
||||
ssh_key_free(key);
|
||||
if(privkey != NULL){
|
||||
memset(ssh_string_data(privkey), 0, ssh_string_len(privkey));
|
||||
}
|
||||
SAFE_FREE(pubkey);
|
||||
SAFE_FREE(privkey);
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Import a private key in OpenSSH (new) format. This format is
|
||||
* typically used with ed25519 keys but can be used for others.
|
||||
*/
|
||||
ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
|
||||
const char *passphrase,
|
||||
ssh_auth_callback auth_fn,
|
||||
void *auth_data)
|
||||
{
|
||||
const char *ptr=text_key;
|
||||
const char *end;
|
||||
char *base64;
|
||||
int cmp;
|
||||
int rc;
|
||||
int i;
|
||||
ssh_buffer buffer = NULL, privkey_buffer=NULL;
|
||||
char *magic = NULL, *ciphername = NULL, *kdfname = NULL;
|
||||
uint32_t nkeys = 0, checkint1, checkint2;
|
||||
ssh_string kdfoptions = NULL;
|
||||
ssh_string pubkey0 = NULL;
|
||||
ssh_string privkeys = NULL;
|
||||
ssh_key key = NULL;
|
||||
|
||||
cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
|
||||
if (cmp != 0){
|
||||
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
|
||||
goto error;
|
||||
}
|
||||
ptr += strlen(OPENSSH_HEADER_BEGIN);
|
||||
while(ptr[0] != '\0' && !isspace((int)ptr[0])) {
|
||||
ptr++;
|
||||
}
|
||||
end = strstr(ptr, OPENSSH_HEADER_END);
|
||||
if (end == NULL){
|
||||
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
|
||||
goto error;
|
||||
}
|
||||
base64 = malloc(end - ptr + 1);
|
||||
if (base64 == NULL){
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; ptr < end; ptr++){
|
||||
if (!isspace((int)ptr[0])) {
|
||||
base64[i] = ptr[0];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
base64[i] = '\0';
|
||||
buffer = base64_to_bin(base64);
|
||||
SAFE_FREE(base64);
|
||||
if (buffer == NULL){
|
||||
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_unpack(buffer, "PssSdSS",
|
||||
strlen(OPENSSH_AUTH_MAGIC) + 1,
|
||||
&magic,
|
||||
&ciphername,
|
||||
&kdfname,
|
||||
&kdfoptions,
|
||||
&nkeys,
|
||||
&pubkey0,
|
||||
&privkeys);
|
||||
if (rc == SSH_ERROR){
|
||||
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
|
||||
goto error;
|
||||
}
|
||||
cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
|
||||
if (cmp != 0){
|
||||
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_INFO, "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", ciphername, kdfname, nkeys);
|
||||
if (strcmp(ciphername, "none") != 0){
|
||||
SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
|
||||
goto error;
|
||||
}
|
||||
if (nkeys != 1){
|
||||
SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
|
||||
goto error;
|
||||
}
|
||||
|
||||
privkey_buffer = ssh_buffer_new();
|
||||
ssh_buffer_add_data(privkey_buffer, ssh_string_data(privkeys), ssh_string_len(privkeys));
|
||||
rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
|
||||
if (rc == SSH_ERROR || checkint1 != checkint2){
|
||||
SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
|
||||
goto error;
|
||||
}
|
||||
rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
|
||||
|
||||
error:
|
||||
if(buffer != NULL){
|
||||
ssh_buffer_free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
if(privkey_buffer != NULL){
|
||||
ssh_buffer_free(privkey_buffer);
|
||||
privkey_buffer = NULL;
|
||||
}
|
||||
SAFE_FREE(magic);
|
||||
SAFE_FREE(ciphername);
|
||||
SAFE_FREE(kdfname);
|
||||
SAFE_FREE(kdfoptions);
|
||||
SAFE_FREE(pubkey0);
|
||||
SAFE_FREE(privkeys);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
Загрузка…
Ссылка в новой задаче
Block a user