mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-06 10:27:22 +09:00
Summary: This patch adds support for mbedTLS as a crypto backend for libssh. mbedTLS is an SSL/TLS library that has been designed to mainly be used in embedded systems. It is loosely coupled and has a low memory footprint. mbedTLS also provides a cryptography library (libmbedcrypto) that can be used without the TLS modules. The patch is unfortunately quite big, since several new files had to be added. DSA is disabled at compile time, since mbedTLS doesn't support DSA Patch review and feedback would be appreciated, and if any issues or suggestions appear, I'm willing to work on them. Signed-off-by: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr> Test Plan: * The patch has been tested with a Debug and MinSizeRel build, with libssh unit tests, client tests and the pkd tests. * All the tests have been run with valgrind's memcheck, drd and helgrind tools. * The examples/samplessh client works when built with the patch. Reviewers: asn, aris Subscribers: simonsj Differential Revision: https://bugs.libssh.org/D1
1862 lines
48 KiB
C
1862 lines
48 KiB
C
/*
|
|
* pki.c
|
|
* This file is part of the SSH Library
|
|
*
|
|
* Copyright (c) 2010 by Aris Adamantiadis
|
|
* Copyright (c) 2011-2013 Andreas Schneider <asn@cryptomilk.org>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @defgroup libssh_pki The SSH Public Key Infrastructure
|
|
* @ingroup libssh
|
|
*
|
|
* Functions for the creation, importation and manipulation of public and
|
|
* private keys in the context of the SSH protocol
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef _WIN32
|
|
# ifdef HAVE_IO_H
|
|
# include <io.h>
|
|
# undef open
|
|
# define open _open
|
|
# undef close
|
|
# define close _close
|
|
# undef read
|
|
# define read _read
|
|
# undef unlink
|
|
# define unlink _unlink
|
|
# endif /* HAVE_IO_H */
|
|
#endif
|
|
|
|
#include "libssh/libssh.h"
|
|
#include "libssh/session.h"
|
|
#include "libssh/priv.h"
|
|
#include "libssh/pki.h"
|
|
#include "libssh/pki_priv.h"
|
|
#include "libssh/keys.h"
|
|
#include "libssh/buffer.h"
|
|
#include "libssh/misc.h"
|
|
#include "libssh/agent.h"
|
|
|
|
enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) {
|
|
if (strncmp(privkey, DSA_HEADER_BEGIN, strlen(DSA_HEADER_BEGIN)) == 0) {
|
|
return SSH_KEYTYPE_DSS;
|
|
}
|
|
|
|
if (strncmp(privkey, RSA_HEADER_BEGIN, strlen(RSA_HEADER_BEGIN)) == 0) {
|
|
return SSH_KEYTYPE_RSA;
|
|
}
|
|
|
|
if (strncmp(privkey, ECDSA_HEADER_BEGIN, strlen(ECDSA_HEADER_BEGIN)) == 0) {
|
|
return SSH_KEYTYPE_ECDSA;
|
|
}
|
|
|
|
return SSH_KEYTYPE_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
|
|
*
|
|
* @param[in] key the ssh_key whose ECDSA name to get
|
|
*
|
|
* @returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
|
|
*
|
|
* @returns "unknown" if the ECDSA key name is not known
|
|
*/
|
|
const char *ssh_pki_key_ecdsa_name(const ssh_key key)
|
|
{
|
|
#ifdef HAVE_ECC /* FIXME Better ECC check needed */
|
|
return pki_key_ecdsa_nid_to_name(key->ecdsa_nid);
|
|
#else
|
|
(void) key; /* unused */
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief creates a new empty SSH key
|
|
* @returns an empty ssh_key handle, or NULL on error.
|
|
*/
|
|
ssh_key ssh_key_new (void) {
|
|
ssh_key ptr = malloc (sizeof (struct ssh_key_struct));
|
|
if (ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
ZERO_STRUCTP(ptr);
|
|
return ptr;
|
|
}
|
|
|
|
ssh_key ssh_key_dup(const ssh_key key)
|
|
{
|
|
if (key == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pki_key_dup(key, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief clean up the key and deallocate all existing keys
|
|
* @param[in] key ssh_key to clean
|
|
*/
|
|
void ssh_key_clean (ssh_key key){
|
|
if(key == NULL)
|
|
return;
|
|
#ifdef HAVE_LIBGCRYPT
|
|
if(key->dsa) gcry_sexp_release(key->dsa);
|
|
if(key->rsa) gcry_sexp_release(key->rsa);
|
|
if(key->ecdsa) gcry_sexp_release(key->ecdsa);
|
|
#elif defined HAVE_LIBCRYPTO
|
|
if(key->dsa) DSA_free(key->dsa);
|
|
if(key->rsa) RSA_free(key->rsa);
|
|
#ifdef HAVE_OPENSSL_ECC
|
|
if(key->ecdsa) EC_KEY_free(key->ecdsa);
|
|
#endif /* HAVE_OPENSSL_ECC */
|
|
#elif defined HAVE_LIBMBEDCRYPTO
|
|
if (key->rsa != NULL) {
|
|
mbedtls_pk_free(key->rsa);
|
|
SAFE_FREE(key->rsa);
|
|
}
|
|
|
|
if (key->ecdsa != NULL) {
|
|
mbedtls_ecdsa_free(key->ecdsa);
|
|
SAFE_FREE(key->ecdsa);
|
|
}
|
|
#endif
|
|
if (key->ed25519_privkey != NULL){
|
|
BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey));
|
|
SAFE_FREE(key->ed25519_privkey);
|
|
}
|
|
SAFE_FREE(key->ed25519_pubkey);
|
|
if (key->cert != NULL) {
|
|
ssh_buffer_free(key->cert);
|
|
}
|
|
key->cert_type = SSH_KEYTYPE_UNKNOWN;
|
|
key->flags=SSH_KEY_FLAG_EMPTY;
|
|
key->type=SSH_KEYTYPE_UNKNOWN;
|
|
key->ecdsa_nid = 0;
|
|
key->type_c=NULL;
|
|
key->dsa = NULL;
|
|
key->rsa = NULL;
|
|
key->ecdsa = NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief deallocate a SSH key
|
|
* @param[in] key ssh_key handle to free
|
|
*/
|
|
void ssh_key_free (ssh_key key){
|
|
if(key){
|
|
ssh_key_clean(key);
|
|
SAFE_FREE(key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief returns the type of a ssh key
|
|
* @param[in] key the ssh_key handle
|
|
* @returns one of SSH_KEYTYPE_RSA,SSH_KEYTYPE_DSS,SSH_KEYTYPE_RSA1
|
|
* @returns SSH_KEYTYPE_UNKNOWN if the type is unknown
|
|
*/
|
|
enum ssh_keytypes_e ssh_key_type(const ssh_key key){
|
|
if (key == NULL) {
|
|
return SSH_KEYTYPE_UNKNOWN;
|
|
}
|
|
return key->type;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a key type to a string.
|
|
*
|
|
* @param[in] type The type to convert.
|
|
*
|
|
* @return A string for the keytype or NULL if unknown.
|
|
*/
|
|
const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
|
|
switch (type) {
|
|
case SSH_KEYTYPE_DSS:
|
|
return "ssh-dss";
|
|
case SSH_KEYTYPE_RSA:
|
|
return "ssh-rsa";
|
|
case SSH_KEYTYPE_RSA1:
|
|
return "ssh-rsa1";
|
|
case SSH_KEYTYPE_ECDSA:
|
|
return "ssh-ecdsa";
|
|
case SSH_KEYTYPE_ED25519:
|
|
return "ssh-ed25519";
|
|
case SSH_KEYTYPE_DSS_CERT01:
|
|
return "ssh-dss-cert-v01@openssh.com";
|
|
case SSH_KEYTYPE_RSA_CERT01:
|
|
return "ssh-rsa-cert-v01@openssh.com";
|
|
case SSH_KEYTYPE_UNKNOWN:
|
|
return NULL;
|
|
}
|
|
|
|
/* We should never reach this */
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a ssh key name to a ssh key type.
|
|
*
|
|
* @param[in] name The name to convert.
|
|
*
|
|
* @return The enum ssh key type.
|
|
*/
|
|
enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
|
|
if (name == NULL) {
|
|
return SSH_KEYTYPE_UNKNOWN;
|
|
}
|
|
|
|
if (strcmp(name, "rsa1") == 0) {
|
|
return SSH_KEYTYPE_RSA1;
|
|
} else if (strcmp(name, "rsa") == 0) {
|
|
return SSH_KEYTYPE_RSA;
|
|
} else if (strcmp(name, "dsa") == 0) {
|
|
return SSH_KEYTYPE_DSS;
|
|
} else if (strcmp(name, "ssh-rsa1") == 0) {
|
|
return SSH_KEYTYPE_RSA1;
|
|
} else if (strcmp(name, "ssh-rsa") == 0) {
|
|
return SSH_KEYTYPE_RSA;
|
|
} else if (strcmp(name, "ssh-dss") == 0) {
|
|
return SSH_KEYTYPE_DSS;
|
|
} else if (strcmp(name, "ssh-ecdsa") == 0
|
|
|| strcmp(name, "ecdsa") == 0
|
|
|| strcmp(name, "ecdsa-sha2-nistp256") == 0
|
|
|| strcmp(name, "ecdsa-sha2-nistp384") == 0
|
|
|| strcmp(name, "ecdsa-sha2-nistp521") == 0) {
|
|
return SSH_KEYTYPE_ECDSA;
|
|
} else if (strcmp(name, "ssh-ed25519") == 0){
|
|
return SSH_KEYTYPE_ED25519;
|
|
} else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
|
|
return SSH_KEYTYPE_DSS_CERT01;
|
|
} else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
|
|
return SSH_KEYTYPE_RSA_CERT01;
|
|
}
|
|
|
|
return SSH_KEYTYPE_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the key has/is a public key.
|
|
*
|
|
* @param[in] k The key to check.
|
|
*
|
|
* @return 1 if it is a public key, 0 if not.
|
|
*/
|
|
int ssh_key_is_public(const ssh_key k) {
|
|
if (k == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (k->flags & SSH_KEY_FLAG_PUBLIC) == SSH_KEY_FLAG_PUBLIC;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the key is a private key.
|
|
*
|
|
* @param[in] k The key to check.
|
|
*
|
|
* @return 1 if it is a private key, 0 if not.
|
|
*/
|
|
int ssh_key_is_private(const ssh_key k) {
|
|
if (k == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (k->flags & SSH_KEY_FLAG_PRIVATE) == SSH_KEY_FLAG_PRIVATE;
|
|
}
|
|
|
|
/**
|
|
* @brief Compare keys if they are equal.
|
|
*
|
|
* @param[in] k1 The first key to compare.
|
|
*
|
|
* @param[in] k2 The second key to compare.
|
|
*
|
|
* @param[in] what What part or type of the key do you want to compare.
|
|
*
|
|
* @return 0 if equal, 1 if not.
|
|
*/
|
|
int ssh_key_cmp(const ssh_key k1,
|
|
const ssh_key k2,
|
|
enum ssh_keycmp_e what)
|
|
{
|
|
if (k1 == NULL || k2 == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
if (k1->type != k2->type) {
|
|
SSH_LOG(SSH_LOG_WARN, "key types don't match!");
|
|
return 1;
|
|
}
|
|
|
|
if (what == SSH_KEY_CMP_PRIVATE) {
|
|
if (!ssh_key_is_private(k1) ||
|
|
!ssh_key_is_private(k2)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (k1->type == SSH_KEYTYPE_ED25519) {
|
|
return pki_ed25519_key_cmp(k1, k2, what);
|
|
}
|
|
|
|
return pki_key_compare(k1, k2, what);
|
|
}
|
|
|
|
ssh_signature ssh_signature_new(void)
|
|
{
|
|
struct ssh_signature_struct *sig;
|
|
|
|
sig = malloc(sizeof(struct ssh_signature_struct));
|
|
if (sig == NULL) {
|
|
return NULL;
|
|
}
|
|
ZERO_STRUCTP(sig);
|
|
|
|
return sig;
|
|
}
|
|
|
|
void ssh_signature_free(ssh_signature sig)
|
|
{
|
|
if (sig == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch(sig->type) {
|
|
case SSH_KEYTYPE_DSS:
|
|
#ifdef HAVE_LIBGCRYPT
|
|
gcry_sexp_release(sig->dsa_sig);
|
|
#elif defined HAVE_LIBCRYPTO
|
|
DSA_SIG_free(sig->dsa_sig);
|
|
#endif
|
|
break;
|
|
case SSH_KEYTYPE_RSA:
|
|
case SSH_KEYTYPE_RSA1:
|
|
#ifdef HAVE_LIBGCRYPT
|
|
gcry_sexp_release(sig->rsa_sig);
|
|
#elif defined HAVE_LIBCRYPTO
|
|
SAFE_FREE(sig->rsa_sig);
|
|
#elif defined HAVE_LIBMBEDCRYPTO
|
|
SAFE_FREE(sig->rsa_sig);
|
|
#endif
|
|
break;
|
|
case SSH_KEYTYPE_ECDSA:
|
|
#ifdef HAVE_GCRYPT_ECC
|
|
gcry_sexp_release(sig->ecdsa_sig);
|
|
#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC)
|
|
ECDSA_SIG_free(sig->ecdsa_sig);
|
|
#elif defined HAVE_LIBMBEDCRYPTO
|
|
bignum_free(sig->ecdsa_sig.r);
|
|
bignum_free(sig->ecdsa_sig.s);
|
|
#endif
|
|
break;
|
|
case SSH_KEYTYPE_ED25519:
|
|
SAFE_FREE(sig->ed25519_sig);
|
|
break;
|
|
case SSH_KEYTYPE_DSS_CERT01:
|
|
case SSH_KEYTYPE_RSA_CERT01:
|
|
case SSH_KEYTYPE_UNKNOWN:
|
|
break;
|
|
}
|
|
|
|
SAFE_FREE(sig);
|
|
}
|
|
|
|
/**
|
|
* @brief import a base64 formated key from a memory c-string
|
|
*
|
|
* @param[in] b64_key The c-string holding the base64 encoded key
|
|
*
|
|
* @param[in] passphrase The passphrase to decrypt the key, or NULL
|
|
*
|
|
* @param[in] auth_fn An auth function you may want to use or NULL.
|
|
*
|
|
* @param[in] auth_data Private data passed to the auth function.
|
|
*
|
|
* @param[out] pkey A pointer where the allocated key can be stored. You
|
|
* need to free the memory.
|
|
*
|
|
* @return SSH_ERROR in case of error, SSH_OK otherwise.
|
|
*
|
|
* @see ssh_key_free()
|
|
*/
|
|
int ssh_pki_import_privkey_base64(const char *b64_key,
|
|
const char *passphrase,
|
|
ssh_auth_callback auth_fn,
|
|
void *auth_data,
|
|
ssh_key *pkey)
|
|
{
|
|
ssh_key key;
|
|
int cmp;
|
|
|
|
if (b64_key == NULL || pkey == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
if (b64_key == NULL || !*b64_key) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
SSH_LOG(SSH_LOG_INFO,
|
|
"Trying to decode privkey passphrase=%s",
|
|
passphrase ? "true" : "false");
|
|
|
|
/* 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;
|
|
}
|
|
|
|
*pkey = key;
|
|
|
|
return SSH_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Import a key from a file.
|
|
*
|
|
* @param[in] filename The filename of the the private key.
|
|
*
|
|
* @param[in] passphrase The passphrase to decrypt the private key. Set to NULL
|
|
* if none is needed or it is unknown.
|
|
*
|
|
* @param[in] auth_fn An auth function you may want to use or NULL.
|
|
*
|
|
* @param[in] auth_data Private data passed to the auth function.
|
|
*
|
|
* @param[out] pkey A pointer to store the allocated ssh_key. You need to
|
|
* free the key.
|
|
*
|
|
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
|
|
* denied, SSH_ERROR otherwise.
|
|
*
|
|
* @see ssh_key_free()
|
|
**/
|
|
int ssh_pki_import_privkey_file(const char *filename,
|
|
const char *passphrase,
|
|
ssh_auth_callback auth_fn,
|
|
void *auth_data,
|
|
ssh_key *pkey) {
|
|
struct stat sb;
|
|
char *key_buf;
|
|
FILE *file;
|
|
off_t size;
|
|
int rc;
|
|
|
|
if (pkey == NULL || filename == NULL || *filename == '\0') {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
file = fopen(filename, "rb");
|
|
if (file == NULL) {
|
|
SSH_LOG(SSH_LOG_WARN,
|
|
"Error opening %s: %s",
|
|
filename,
|
|
strerror(errno));
|
|
return SSH_EOF;
|
|
}
|
|
|
|
rc = fstat(fileno(file), &sb);
|
|
if (rc < 0) {
|
|
fclose(file);
|
|
SSH_LOG(SSH_LOG_WARN,
|
|
"Error getting stat of %s: %s",
|
|
filename,
|
|
strerror(errno));
|
|
switch (errno) {
|
|
case ENOENT:
|
|
case EACCES:
|
|
return SSH_EOF;
|
|
}
|
|
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
if (sb.st_size > MAX_PRIVKEY_SIZE) {
|
|
SSH_LOG(SSH_LOG_WARN,
|
|
"Private key is bigger than 4M.");
|
|
fclose(file);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
key_buf = malloc(sb.st_size + 1);
|
|
if (key_buf == NULL) {
|
|
fclose(file);
|
|
SSH_LOG(SSH_LOG_WARN, "Out of memory!");
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
size = fread(key_buf, 1, sb.st_size, file);
|
|
fclose(file);
|
|
|
|
if (size != sb.st_size) {
|
|
SAFE_FREE(key_buf);
|
|
SSH_LOG(SSH_LOG_WARN,
|
|
"Error reading %s: %s",
|
|
filename,
|
|
strerror(errno));
|
|
return SSH_ERROR;
|
|
}
|
|
key_buf[size] = 0;
|
|
|
|
rc = ssh_pki_import_privkey_base64(key_buf,
|
|
passphrase,
|
|
auth_fn,
|
|
auth_data,
|
|
pkey);
|
|
|
|
SAFE_FREE(key_buf);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Export a private key to a pem file on disk, or OpenSSH format for
|
|
* keytype ssh-ed25519
|
|
*
|
|
* @param[in] privkey The private key to export.
|
|
*
|
|
* @param[in] passphrase The passphrase to use to encrypt the key with or
|
|
* NULL. An empty string means no passphrase.
|
|
*
|
|
* @param[in] auth_fn An auth function you may want to use or NULL.
|
|
*
|
|
* @param[in] auth_data Private data passed to the auth function.
|
|
*
|
|
* @param[in] filename The path where to store the pem file.
|
|
*
|
|
* @return SSH_OK on success, SSH_ERROR on error.
|
|
*/
|
|
int ssh_pki_export_privkey_file(const ssh_key privkey,
|
|
const char *passphrase,
|
|
ssh_auth_callback auth_fn,
|
|
void *auth_data,
|
|
const char *filename)
|
|
{
|
|
ssh_string blob;
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
if (privkey == NULL || !ssh_key_is_private(privkey)) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
fp = fopen(filename, "wb");
|
|
if (fp == NULL) {
|
|
SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s",
|
|
filename, strerror(errno));
|
|
return SSH_EOF;
|
|
}
|
|
|
|
if (privkey->type == SSH_KEYTYPE_ED25519){
|
|
blob = ssh_pki_openssh_privkey_export(privkey,
|
|
passphrase,
|
|
auth_fn,
|
|
auth_data);
|
|
} else {
|
|
blob = pki_private_key_to_pem(privkey,
|
|
passphrase,
|
|
auth_fn,
|
|
auth_data);
|
|
}
|
|
if (blob == NULL) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
rc = fwrite(ssh_string_data(blob), ssh_string_len(blob), 1, fp);
|
|
ssh_string_free(blob);
|
|
if (rc != 1 || ferror(fp)) {
|
|
fclose(fp);
|
|
unlink(filename);
|
|
return SSH_ERROR;
|
|
}
|
|
fclose(fp);
|
|
|
|
return SSH_OK;
|
|
}
|
|
|
|
/* temporary function to migrate seemlessly to ssh_key */
|
|
ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
|
|
ssh_public_key pub;
|
|
ssh_key tmp;
|
|
|
|
if(key == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
tmp = ssh_key_dup(key);
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pub = malloc(sizeof(struct ssh_public_key_struct));
|
|
if (pub == NULL) {
|
|
ssh_key_free(tmp);
|
|
return NULL;
|
|
}
|
|
ZERO_STRUCTP(pub);
|
|
|
|
pub->type = tmp->type;
|
|
pub->type_c = tmp->type_c;
|
|
|
|
pub->dsa_pub = tmp->dsa;
|
|
tmp->dsa = NULL;
|
|
pub->rsa_pub = tmp->rsa;
|
|
tmp->rsa = NULL;
|
|
|
|
ssh_key_free(tmp);
|
|
|
|
return pub;
|
|
}
|
|
|
|
ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key) {
|
|
ssh_private_key privkey;
|
|
|
|
privkey = malloc(sizeof(struct ssh_private_key_struct));
|
|
if (privkey == NULL) {
|
|
ssh_key_free(key);
|
|
return NULL;
|
|
}
|
|
|
|
privkey->type = key->type;
|
|
privkey->dsa_priv = key->dsa;
|
|
privkey->rsa_priv = key->rsa;
|
|
|
|
return privkey;
|
|
}
|
|
|
|
static int pki_import_pubkey_buffer(ssh_buffer buffer,
|
|
enum ssh_keytypes_e type,
|
|
ssh_key *pkey) {
|
|
ssh_key key;
|
|
int rc;
|
|
|
|
key = ssh_key_new();
|
|
if (key == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
key->type = type;
|
|
key->type_c = ssh_key_type_to_char(type);
|
|
key->flags = SSH_KEY_FLAG_PUBLIC;
|
|
|
|
switch (type) {
|
|
case SSH_KEYTYPE_DSS:
|
|
{
|
|
ssh_string p;
|
|
ssh_string q;
|
|
ssh_string g;
|
|
ssh_string pubkey;
|
|
|
|
p = ssh_buffer_get_ssh_string(buffer);
|
|
if (p == NULL) {
|
|
goto fail;
|
|
}
|
|
q = ssh_buffer_get_ssh_string(buffer);
|
|
if (q == NULL) {
|
|
ssh_string_burn(p);
|
|
ssh_string_free(p);
|
|
|
|
goto fail;
|
|
}
|
|
g = ssh_buffer_get_ssh_string(buffer);
|
|
if (g == NULL) {
|
|
ssh_string_burn(p);
|
|
ssh_string_free(p);
|
|
ssh_string_burn(q);
|
|
ssh_string_free(q);
|
|
|
|
goto fail;
|
|
}
|
|
pubkey = ssh_buffer_get_ssh_string(buffer);
|
|
if (pubkey == NULL) {
|
|
ssh_string_burn(p);
|
|
ssh_string_free(p);
|
|
ssh_string_burn(q);
|
|
ssh_string_free(q);
|
|
ssh_string_burn(g);
|
|
ssh_string_free(g);
|
|
|
|
goto fail;
|
|
}
|
|
|
|
rc = pki_pubkey_build_dss(key, p, q, g, pubkey);
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p));
|
|
ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q));
|
|
ssh_print_hexa("g", ssh_string_data(g), ssh_string_len(g));
|
|
#endif
|
|
ssh_string_burn(p);
|
|
ssh_string_free(p);
|
|
ssh_string_burn(q);
|
|
ssh_string_free(q);
|
|
ssh_string_burn(g);
|
|
ssh_string_free(g);
|
|
ssh_string_burn(pubkey);
|
|
ssh_string_free(pubkey);
|
|
if (rc == SSH_ERROR) {
|
|
goto fail;
|
|
}
|
|
}
|
|
break;
|
|
case SSH_KEYTYPE_RSA:
|
|
case SSH_KEYTYPE_RSA1:
|
|
{
|
|
ssh_string e;
|
|
ssh_string n;
|
|
|
|
e = ssh_buffer_get_ssh_string(buffer);
|
|
if (e == NULL) {
|
|
goto fail;
|
|
}
|
|
n = ssh_buffer_get_ssh_string(buffer);
|
|
if (n == NULL) {
|
|
ssh_string_burn(e);
|
|
ssh_string_free(e);
|
|
|
|
goto fail;
|
|
}
|
|
|
|
rc = pki_pubkey_build_rsa(key, e, n);
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("e", ssh_string_data(e), ssh_string_len(e));
|
|
ssh_print_hexa("n", ssh_string_data(n), ssh_string_len(n));
|
|
#endif
|
|
ssh_string_burn(e);
|
|
ssh_string_free(e);
|
|
ssh_string_burn(n);
|
|
ssh_string_free(n);
|
|
if (rc == SSH_ERROR) {
|
|
goto fail;
|
|
}
|
|
}
|
|
break;
|
|
case SSH_KEYTYPE_ECDSA:
|
|
#ifdef HAVE_ECC
|
|
{
|
|
ssh_string e;
|
|
ssh_string i;
|
|
int nid;
|
|
|
|
i = ssh_buffer_get_ssh_string(buffer);
|
|
if (i == NULL) {
|
|
goto fail;
|
|
}
|
|
nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i));
|
|
ssh_string_free(i);
|
|
if (nid == -1) {
|
|
goto fail;
|
|
}
|
|
|
|
|
|
e = ssh_buffer_get_ssh_string(buffer);
|
|
if (e == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
rc = pki_pubkey_build_ecdsa(key, nid, e);
|
|
ssh_string_burn(e);
|
|
ssh_string_free(e);
|
|
if (rc < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
/* Update key type */
|
|
key->type_c = ssh_pki_key_ecdsa_name(key);
|
|
}
|
|
break;
|
|
#endif
|
|
case SSH_KEYTYPE_ED25519:
|
|
{
|
|
ssh_string pubkey = ssh_buffer_get_ssh_string(buffer);
|
|
if (ssh_string_len(pubkey) != ED25519_PK_LEN) {
|
|
SSH_LOG(SSH_LOG_WARN, "Invalid public key length");
|
|
ssh_string_burn(pubkey);
|
|
ssh_string_free(pubkey);
|
|
goto fail;
|
|
}
|
|
|
|
key->ed25519_pubkey = malloc(ED25519_PK_LEN);
|
|
if (key->ed25519_pubkey == NULL) {
|
|
ssh_string_burn(pubkey);
|
|
ssh_string_free(pubkey);
|
|
goto fail;
|
|
}
|
|
|
|
memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN);
|
|
ssh_string_burn(pubkey);
|
|
ssh_string_free(pubkey);
|
|
}
|
|
break;
|
|
case SSH_KEYTYPE_DSS_CERT01:
|
|
case SSH_KEYTYPE_RSA_CERT01:
|
|
case SSH_KEYTYPE_UNKNOWN:
|
|
default:
|
|
SSH_LOG(SSH_LOG_WARN, "Unknown public key protocol %d", type);
|
|
goto fail;
|
|
}
|
|
|
|
*pkey = key;
|
|
return SSH_OK;
|
|
fail:
|
|
ssh_key_free(key);
|
|
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
static int pki_import_cert_buffer(ssh_buffer buffer,
|
|
enum ssh_keytypes_e type,
|
|
ssh_key *pkey) {
|
|
ssh_buffer cert;
|
|
ssh_string type_s;
|
|
ssh_key key;
|
|
int rc;
|
|
|
|
key = ssh_key_new();
|
|
if (key == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
cert = ssh_buffer_new();
|
|
if (cert == NULL) {
|
|
ssh_key_free(key);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
key->type = type;
|
|
key->type_c = ssh_key_type_to_char(type);
|
|
key->flags = SSH_KEY_FLAG_PUBLIC;
|
|
|
|
/*
|
|
* The cert blob starts with the key type as an ssh_string, but this
|
|
* string has been read out of the buffer to identify the key type.
|
|
* Simply add it again as first element before copying the rest.
|
|
*/
|
|
type_s = ssh_string_from_char(key->type_c);
|
|
if (type_s == NULL) {
|
|
goto fail;
|
|
}
|
|
rc = ssh_buffer_add_ssh_string(cert, type_s);
|
|
ssh_string_free(type_s);
|
|
if (rc != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
rc = ssh_buffer_add_buffer(cert, buffer);
|
|
if (rc != 0) {
|
|
goto fail;
|
|
}
|
|
key->cert = (void*) cert;
|
|
|
|
*pkey = key;
|
|
return SSH_OK;
|
|
|
|
fail:
|
|
ssh_key_free(key);
|
|
ssh_buffer_free(cert);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Import a base64 formated public key from a memory c-string.
|
|
*
|
|
* @param[in] b64_key The base64 key to format.
|
|
*
|
|
* @param[in] type The type of the key to format.
|
|
*
|
|
* @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()
|
|
*/
|
|
int ssh_pki_import_pubkey_base64(const char *b64_key,
|
|
enum ssh_keytypes_e type,
|
|
ssh_key *pkey) {
|
|
ssh_buffer buffer;
|
|
ssh_string type_s;
|
|
int rc;
|
|
|
|
if (b64_key == NULL || pkey == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
buffer = base64_to_bin(b64_key);
|
|
if (buffer == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
type_s = ssh_buffer_get_ssh_string(buffer);
|
|
if (type_s == NULL) {
|
|
ssh_buffer_free(buffer);
|
|
return SSH_ERROR;
|
|
}
|
|
ssh_string_free(type_s);
|
|
|
|
if (type == SSH_KEYTYPE_RSA_CERT01 ||
|
|
type == SSH_KEYTYPE_DSS_CERT01) {
|
|
rc = pki_import_cert_buffer(buffer, type, pkey);
|
|
} else {
|
|
rc = pki_import_pubkey_buffer(buffer, type, pkey);
|
|
}
|
|
ssh_buffer_free(buffer);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @brief Import a public key from a ssh string.
|
|
*
|
|
* @param[in] key_blob The key blob to import as specified in RFC 4253 section
|
|
* 6.6 "Public Key Algorithms".
|
|
*
|
|
* @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()
|
|
*/
|
|
int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
|
|
ssh_key *pkey) {
|
|
ssh_buffer buffer;
|
|
ssh_string type_s = NULL;
|
|
enum ssh_keytypes_e type;
|
|
int rc;
|
|
|
|
if (key_blob == NULL || pkey == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
buffer = ssh_buffer_new();
|
|
if (buffer == NULL) {
|
|
SSH_LOG(SSH_LOG_WARN, "Out of memory!");
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob),
|
|
ssh_string_len(key_blob));
|
|
if (rc < 0) {
|
|
SSH_LOG(SSH_LOG_WARN, "Out of memory!");
|
|
goto fail;
|
|
}
|
|
|
|
type_s = ssh_buffer_get_ssh_string(buffer);
|
|
if (type_s == NULL) {
|
|
SSH_LOG(SSH_LOG_WARN, "Out of memory!");
|
|
goto fail;
|
|
}
|
|
|
|
type = ssh_key_type_from_name(ssh_string_get_char(type_s));
|
|
if (type == SSH_KEYTYPE_UNKNOWN) {
|
|
SSH_LOG(SSH_LOG_WARN, "Unknown key type found!");
|
|
goto fail;
|
|
}
|
|
ssh_string_free(type_s);
|
|
|
|
if (type == SSH_KEYTYPE_RSA_CERT01 ||
|
|
type == SSH_KEYTYPE_DSS_CERT01) {
|
|
rc = pki_import_cert_buffer(buffer, type, pkey);
|
|
} else {
|
|
rc = pki_import_pubkey_buffer(buffer, type, pkey);
|
|
}
|
|
|
|
ssh_buffer_free(buffer);
|
|
|
|
return rc;
|
|
fail:
|
|
ssh_buffer_free(buffer);
|
|
ssh_string_free(type_s);
|
|
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Import a public key from the given filename.
|
|
*
|
|
* @param[in] filename The path to the public key.
|
|
*
|
|
* @param[out] pkey A pointer to store the allocated public key. You need to
|
|
* free the memory.
|
|
*
|
|
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
|
|
* denied, SSH_ERROR otherwise.
|
|
*
|
|
* @see ssh_key_free()
|
|
*/
|
|
int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
|
|
{
|
|
enum ssh_keytypes_e type;
|
|
struct stat sb;
|
|
char *key_buf, *p;
|
|
const char *q;
|
|
FILE *file;
|
|
off_t size;
|
|
int rc;
|
|
|
|
if (pkey == NULL || filename == NULL || *filename == '\0') {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
file = fopen(filename, "rb");
|
|
if (file == NULL) {
|
|
SSH_LOG(SSH_LOG_WARN, "Error opening %s: %s",
|
|
filename, strerror(errno));
|
|
return SSH_EOF;
|
|
}
|
|
|
|
rc = fstat(fileno(file), &sb);
|
|
if (rc < 0) {
|
|
fclose(file);
|
|
SSH_LOG(SSH_LOG_WARN, "Error gettint stat of %s: %s",
|
|
filename, strerror(errno));
|
|
switch (errno) {
|
|
case ENOENT:
|
|
case EACCES:
|
|
return SSH_EOF;
|
|
}
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
if (sb.st_size > MAX_PUBKEY_SIZE) {
|
|
fclose(file);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
key_buf = malloc(sb.st_size + 1);
|
|
if (key_buf == NULL) {
|
|
fclose(file);
|
|
SSH_LOG(SSH_LOG_WARN, "Out of memory!");
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
size = fread(key_buf, 1, sb.st_size, file);
|
|
fclose(file);
|
|
|
|
if (size != sb.st_size) {
|
|
SAFE_FREE(key_buf);
|
|
SSH_LOG(SSH_LOG_WARN, "Error reading %s: %s",
|
|
filename, strerror(errno));
|
|
return SSH_ERROR;
|
|
}
|
|
key_buf[size] = '\0';
|
|
|
|
q = p = key_buf;
|
|
while (!isspace((int)*p)) p++;
|
|
*p = '\0';
|
|
|
|
type = ssh_key_type_from_name(q);
|
|
if (type == SSH_KEYTYPE_UNKNOWN) {
|
|
SAFE_FREE(key_buf);
|
|
return SSH_ERROR;
|
|
}
|
|
q = ++p;
|
|
while (!isspace((int)*p)) p++;
|
|
*p = '\0';
|
|
|
|
rc = ssh_pki_import_pubkey_base64(q, type, pkey);
|
|
SAFE_FREE(key_buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Import a base64 formated certificate from a memory c-string.
|
|
*
|
|
* @param[in] b64_cert The base64 cert to format.
|
|
*
|
|
* @param[in] type The type of the cert to format.
|
|
*
|
|
* @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()
|
|
*/
|
|
int ssh_pki_import_cert_base64(const char *b64_cert,
|
|
enum ssh_keytypes_e type,
|
|
ssh_key *pkey) {
|
|
return ssh_pki_import_pubkey_base64(b64_cert, type, pkey);
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @brief Import a certificate from a ssh string.
|
|
*
|
|
* @param[in] cert_blob The cert blob to import as specified in RFC 4253 section
|
|
* 6.6 "Public Key Algorithms".
|
|
*
|
|
* @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()
|
|
*/
|
|
int ssh_pki_import_cert_blob(const ssh_string cert_blob,
|
|
ssh_key *pkey) {
|
|
return ssh_pki_import_pubkey_blob(cert_blob, pkey);
|
|
}
|
|
|
|
/**
|
|
* @brief Import a certificate from the given filename.
|
|
*
|
|
* @param[in] filename The path to the certificate.
|
|
*
|
|
* @param[out] pkey A pointer to store the allocated certificate. You need to
|
|
* free the memory.
|
|
*
|
|
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
|
|
* denied, SSH_ERROR otherwise.
|
|
*
|
|
* @see ssh_key_free()
|
|
*/
|
|
int ssh_pki_import_cert_file(const char *filename, ssh_key *pkey)
|
|
{
|
|
return ssh_pki_import_pubkey_file(filename, pkey);
|
|
}
|
|
|
|
/**
|
|
* @brief Generates a keypair.
|
|
*
|
|
* @param[in] type Type of key to create
|
|
*
|
|
* @param[in] parameter Parameter to the creation of key:
|
|
* rsa : length of the key in bits (e.g. 1024, 2048, 4096)
|
|
* dsa : length of the key in bits (e.g. 1024, 2048, 3072)
|
|
* ecdsa : bits of the key (e.g. 256, 384, 512)
|
|
* @param[out] pkey A pointer to store the allocated private key. You need
|
|
* to free the memory.
|
|
*
|
|
* @return SSH_OK on success, SSH_ERROR on error.
|
|
*
|
|
* @warning Generating a key pair may take some time.
|
|
*/
|
|
int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
|
|
ssh_key *pkey){
|
|
int rc;
|
|
ssh_key key = ssh_key_new();
|
|
|
|
if (key == NULL) {
|
|
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_RSA:
|
|
case SSH_KEYTYPE_RSA1:
|
|
rc = pki_key_generate_rsa(key, parameter);
|
|
if(rc == SSH_ERROR)
|
|
goto error;
|
|
break;
|
|
case SSH_KEYTYPE_DSS:
|
|
rc = pki_key_generate_dss(key, parameter);
|
|
if(rc == SSH_ERROR)
|
|
goto error;
|
|
break;
|
|
case SSH_KEYTYPE_ECDSA:
|
|
#ifdef HAVE_ECC
|
|
rc = pki_key_generate_ecdsa(key, parameter);
|
|
if (rc == SSH_ERROR) {
|
|
goto error;
|
|
}
|
|
|
|
/* Update key type */
|
|
key->type_c = ssh_pki_key_ecdsa_name(key);
|
|
break;
|
|
#endif
|
|
case SSH_KEYTYPE_ED25519:
|
|
rc = pki_key_generate_ed25519(key);
|
|
if (rc == SSH_ERROR) {
|
|
goto error;
|
|
}
|
|
break;
|
|
case SSH_KEYTYPE_DSS_CERT01:
|
|
case SSH_KEYTYPE_RSA_CERT01:
|
|
case SSH_KEYTYPE_UNKNOWN:
|
|
goto error;
|
|
}
|
|
|
|
*pkey = key;
|
|
return SSH_OK;
|
|
error:
|
|
ssh_key_free(key);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a public key from a private key.
|
|
*
|
|
* @param[in] privkey The private key to get the public key from.
|
|
*
|
|
* @param[out] pkey A pointer to store the newly allocated public key. You
|
|
* NEED to free the key.
|
|
*
|
|
* @return SSH_OK on success, SSH_ERROR on error.
|
|
*
|
|
* @see ssh_key_free()
|
|
*/
|
|
int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
|
|
ssh_key *pkey)
|
|
{
|
|
ssh_key pubkey;
|
|
|
|
if (privkey == NULL || !ssh_key_is_private(privkey)) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
pubkey = pki_key_dup(privkey, 1);
|
|
if (pubkey == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
*pkey = pubkey;
|
|
return SSH_OK;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @brief Create a key_blob from a public key.
|
|
*
|
|
* The "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
|
|
* Algorithms" for any of the supported protocol 2 key types.
|
|
* Encoding of EC keys is described in RFC 5656 section 3.1 "Key
|
|
* Format".
|
|
*
|
|
* @param[in] key A public or private key to create the public ssh_string
|
|
* from.
|
|
*
|
|
* @param[out] pblob A pointer to store the newly allocated key blob. You
|
|
* NEED to free it.
|
|
*
|
|
* @return SSH_OK on success, SSH_ERROR otherwise.
|
|
*
|
|
* @see ssh_string_free()
|
|
*/
|
|
int ssh_pki_export_pubkey_blob(const ssh_key key,
|
|
ssh_string *pblob)
|
|
{
|
|
ssh_string blob;
|
|
|
|
if (key == NULL) {
|
|
return SSH_OK;
|
|
}
|
|
|
|
blob = pki_publickey_to_blob(key);
|
|
if (blob == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
*pblob = blob;
|
|
return SSH_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a public key to a base64 encoded key.
|
|
*
|
|
* @param[in] key The key to hash
|
|
*
|
|
* @param[out] b64_key A pointer to store the allocated base64 encoded key. You
|
|
* need to free the buffer.
|
|
*
|
|
* @return SSH_OK on success, SSH_ERROR on error.
|
|
*
|
|
* @see ssh_string_free_char()
|
|
*/
|
|
int ssh_pki_export_pubkey_base64(const ssh_key key,
|
|
char **b64_key)
|
|
{
|
|
ssh_string key_blob;
|
|
unsigned char *b64;
|
|
|
|
if (key == NULL || b64_key == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
key_blob = pki_publickey_to_blob(key);
|
|
if (key_blob == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
b64 = bin_to_base64(ssh_string_data(key_blob), ssh_string_len(key_blob));
|
|
ssh_string_free(key_blob);
|
|
if (b64 == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
*b64_key = (char *)b64;
|
|
|
|
return SSH_OK;
|
|
}
|
|
|
|
int ssh_pki_export_pubkey_file(const ssh_key key,
|
|
const char *filename)
|
|
{
|
|
char key_buf[4096];
|
|
char host[256];
|
|
char *b64_key;
|
|
char *user;
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
if (key == NULL || filename == NULL || *filename == '\0') {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
user = ssh_get_local_username();
|
|
if (user == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = gethostname(host, sizeof(host));
|
|
if (rc < 0) {
|
|
free(user);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
|
|
if (rc < 0) {
|
|
free(user);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = snprintf(key_buf, sizeof(key_buf),
|
|
"%s %s %s@%s\n",
|
|
key->type_c,
|
|
b64_key,
|
|
user,
|
|
host);
|
|
free(user);
|
|
free(b64_key);
|
|
if (rc < 0) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
fp = fopen(filename, "wb+");
|
|
if (fp == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
rc = fwrite(key_buf, strlen(key_buf), 1, fp);
|
|
if (rc != 1 || ferror(fp)) {
|
|
fclose(fp);
|
|
unlink(filename);
|
|
return SSH_ERROR;
|
|
}
|
|
fclose(fp);
|
|
|
|
return SSH_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Copy the certificate part of a public key into a private key.
|
|
*
|
|
* @param[in] certkey The certificate key.
|
|
*
|
|
* @param[in] privkey The target private key to copy the certificate to.
|
|
*
|
|
* @returns SSH_OK on success, SSH_ERROR otherwise.
|
|
**/
|
|
int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
|
|
ssh_buffer cert_buffer;
|
|
int rc;
|
|
|
|
if (certkey == NULL || privkey == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
if (privkey->cert != NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
if (certkey->cert == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
cert_buffer = ssh_buffer_new();
|
|
if (cert_buffer == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_buffer_add_buffer(cert_buffer, certkey->cert);
|
|
if (rc != 0) {
|
|
ssh_buffer_free(cert_buffer);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
privkey->cert = cert_buffer;
|
|
privkey->cert_type = certkey->type;
|
|
return SSH_OK;
|
|
}
|
|
|
|
int ssh_pki_export_pubkey_rsa1(const ssh_key key,
|
|
const char *host,
|
|
char *rsa1,
|
|
size_t rsa1_len)
|
|
{
|
|
return pki_export_pubkey_rsa1(key, host, rsa1, rsa1_len);
|
|
}
|
|
|
|
int ssh_pki_export_signature_blob(const ssh_signature sig,
|
|
ssh_string *sig_blob)
|
|
{
|
|
ssh_buffer buf = NULL;
|
|
ssh_string str;
|
|
int rc;
|
|
|
|
if (sig == NULL || sig_blob == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
buf = ssh_buffer_new();
|
|
if (buf == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
str = ssh_string_from_char(sig->type_c);
|
|
if (str == NULL) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_buffer_add_ssh_string(buf, str);
|
|
ssh_string_free(str);
|
|
if (rc < 0) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
str = pki_signature_to_blob(sig);
|
|
if (str == NULL) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_buffer_add_ssh_string(buf, str);
|
|
ssh_string_free(str);
|
|
if (rc < 0) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
str = ssh_string_new(ssh_buffer_get_len(buf));
|
|
if (str == NULL) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
|
|
ssh_buffer_free(buf);
|
|
|
|
*sig_blob = str;
|
|
|
|
return SSH_OK;
|
|
}
|
|
|
|
int ssh_pki_import_signature_blob(const ssh_string sig_blob,
|
|
const ssh_key pubkey,
|
|
ssh_signature *psig)
|
|
{
|
|
ssh_signature sig;
|
|
enum ssh_keytypes_e type;
|
|
ssh_string str;
|
|
ssh_buffer buf;
|
|
int rc;
|
|
|
|
if (sig_blob == NULL || psig == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
buf = ssh_buffer_new();
|
|
if (buf == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
rc = ssh_buffer_add_data(buf,
|
|
ssh_string_data(sig_blob),
|
|
ssh_string_len(sig_blob));
|
|
if (rc < 0) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
str = ssh_buffer_get_ssh_string(buf);
|
|
if (str == NULL) {
|
|
ssh_buffer_free(buf);
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
type = ssh_key_type_from_name(ssh_string_get_char(str));
|
|
ssh_string_free(str);
|
|
|
|
str = ssh_buffer_get_ssh_string(buf);
|
|
ssh_buffer_free(buf);
|
|
if (str == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
sig = pki_signature_from_blob(pubkey, str, type);
|
|
ssh_string_free(str);
|
|
if (sig == NULL) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
*psig = sig;
|
|
return SSH_OK;
|
|
}
|
|
|
|
int ssh_pki_signature_verify_blob(ssh_session session,
|
|
ssh_string sig_blob,
|
|
const ssh_key key,
|
|
unsigned char *digest,
|
|
size_t dlen)
|
|
{
|
|
ssh_signature sig;
|
|
int rc;
|
|
|
|
rc = ssh_pki_import_signature_blob(sig_blob, key, &sig);
|
|
if (rc < 0) {
|
|
return SSH_ERROR;
|
|
}
|
|
|
|
SSH_LOG(SSH_LOG_FUNCTIONS,
|
|
"Going to verify a %s type signature",
|
|
key->type_c);
|
|
|
|
|
|
if (key->type == SSH_KEYTYPE_ECDSA) {
|
|
#if HAVE_ECC
|
|
unsigned char ehash[EVP_DIGEST_LEN] = {0};
|
|
uint32_t elen;
|
|
|
|
evp(key->ecdsa_nid, digest, dlen, ehash, &elen);
|
|
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("Hash to be verified with ecdsa",
|
|
ehash, elen);
|
|
#endif
|
|
|
|
rc = pki_signature_verify(session,
|
|
sig,
|
|
key,
|
|
ehash,
|
|
elen);
|
|
#endif
|
|
} else if (key->type == SSH_KEYTYPE_ED25519) {
|
|
rc = pki_signature_verify(session, sig, key, digest, dlen);
|
|
} else {
|
|
unsigned char hash[SHA_DIGEST_LEN] = {0};
|
|
|
|
sha1(digest, dlen, hash);
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa(key->type == SSH_KEYTYPE_DSS
|
|
? "Hash to be verified with DSA"
|
|
: "Hash to be verified with RSA",
|
|
hash,
|
|
SHA_DIGEST_LEN);
|
|
#endif
|
|
|
|
rc = pki_signature_verify(session,
|
|
sig,
|
|
key,
|
|
hash,
|
|
SHA_DIGEST_LEN);
|
|
}
|
|
|
|
ssh_signature_free(sig);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* This function signs the session id as a string then
|
|
* the content of sigbuf */
|
|
ssh_string ssh_pki_do_sign(ssh_session session,
|
|
ssh_buffer sigbuf,
|
|
const ssh_key privkey) {
|
|
struct ssh_crypto_struct *crypto =
|
|
session->current_crypto ? session->current_crypto :
|
|
session->next_crypto;
|
|
ssh_signature sig = NULL;
|
|
ssh_string sig_blob;
|
|
ssh_string session_id;
|
|
int rc;
|
|
|
|
if (privkey == NULL || !ssh_key_is_private(privkey)) {
|
|
return NULL;
|
|
}
|
|
|
|
session_id = ssh_string_new(crypto->digest_len);
|
|
if (session_id == NULL) {
|
|
return NULL;
|
|
}
|
|
ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
|
|
|
|
if (privkey->type == SSH_KEYTYPE_ECDSA) {
|
|
#ifdef HAVE_ECC
|
|
unsigned char ehash[EVP_DIGEST_LEN] = {0};
|
|
uint32_t elen;
|
|
EVPCTX ctx;
|
|
|
|
ctx = evp_init(privkey->ecdsa_nid);
|
|
if (ctx == NULL) {
|
|
ssh_string_free(session_id);
|
|
return NULL;
|
|
}
|
|
|
|
evp_update(ctx, session_id, ssh_string_len(session_id) + 4);
|
|
evp_update(ctx, ssh_buffer_get(sigbuf), ssh_buffer_get_len(sigbuf));
|
|
evp_final(ctx, ehash, &elen);
|
|
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("Hash being signed", ehash, elen);
|
|
#endif
|
|
|
|
sig = pki_do_sign(privkey, ehash, elen);
|
|
#endif
|
|
} else if (privkey->type == SSH_KEYTYPE_ED25519){
|
|
ssh_buffer buf;
|
|
|
|
buf = ssh_buffer_new();
|
|
if (buf == NULL) {
|
|
ssh_string_free(session_id);
|
|
return NULL;
|
|
}
|
|
|
|
ssh_buffer_set_secure(buf);
|
|
rc = ssh_buffer_pack(buf,
|
|
"SP",
|
|
session_id,
|
|
ssh_buffer_get_len(sigbuf), ssh_buffer_get(sigbuf));
|
|
if (rc != SSH_OK) {
|
|
ssh_string_free(session_id);
|
|
ssh_buffer_free(buf);
|
|
return NULL;
|
|
}
|
|
|
|
sig = pki_do_sign(privkey,
|
|
ssh_buffer_get(buf),
|
|
ssh_buffer_get_len(buf));
|
|
ssh_buffer_free(buf);
|
|
} else {
|
|
unsigned char hash[SHA_DIGEST_LEN] = {0};
|
|
SHACTX ctx;
|
|
|
|
ctx = sha1_init();
|
|
if (ctx == NULL) {
|
|
ssh_string_free(session_id);
|
|
return NULL;
|
|
}
|
|
|
|
sha1_update(ctx, session_id, ssh_string_len(session_id) + 4);
|
|
sha1_update(ctx, ssh_buffer_get(sigbuf), ssh_buffer_get_len(sigbuf));
|
|
sha1_final(hash, ctx);
|
|
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
|
|
#endif
|
|
|
|
sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN);
|
|
}
|
|
ssh_string_free(session_id);
|
|
if (sig == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rc = ssh_pki_export_signature_blob(sig, &sig_blob);
|
|
ssh_signature_free(sig);
|
|
if (rc < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return sig_blob;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
ssh_string ssh_pki_do_sign_agent(ssh_session session,
|
|
struct ssh_buffer_struct *buf,
|
|
const ssh_key pubkey) {
|
|
struct ssh_crypto_struct *crypto;
|
|
ssh_string session_id;
|
|
ssh_string sig_blob;
|
|
ssh_buffer sig_buf;
|
|
int rc;
|
|
|
|
if (session->current_crypto) {
|
|
crypto = session->current_crypto;
|
|
} else {
|
|
crypto = session->next_crypto;
|
|
}
|
|
|
|
/* prepend session identifier */
|
|
session_id = ssh_string_new(crypto->digest_len);
|
|
if (session_id == NULL) {
|
|
return NULL;
|
|
}
|
|
ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
|
|
|
|
sig_buf = ssh_buffer_new();
|
|
if (sig_buf == NULL) {
|
|
ssh_string_free(session_id);
|
|
return NULL;
|
|
}
|
|
|
|
rc = ssh_buffer_add_ssh_string(sig_buf, session_id);
|
|
if (rc < 0) {
|
|
ssh_string_free(session_id);
|
|
ssh_buffer_free(sig_buf);
|
|
return NULL;
|
|
}
|
|
ssh_string_free(session_id);
|
|
|
|
/* append out buffer */
|
|
if (ssh_buffer_add_buffer(sig_buf, buf) < 0) {
|
|
ssh_buffer_free(sig_buf);
|
|
return NULL;
|
|
}
|
|
|
|
/* create signature */
|
|
sig_blob = ssh_agent_sign_data(session, pubkey, sig_buf);
|
|
|
|
ssh_buffer_free(sig_buf);
|
|
|
|
return sig_blob;
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
#ifdef WITH_SERVER
|
|
ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
|
|
const ssh_key privkey)
|
|
{
|
|
struct ssh_crypto_struct *crypto;
|
|
ssh_signature sig = NULL;
|
|
ssh_string sig_blob;
|
|
int rc;
|
|
|
|
if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) {
|
|
return NULL;
|
|
}
|
|
crypto = session->next_crypto ? session->next_crypto :
|
|
session->current_crypto;
|
|
|
|
if (crypto->secret_hash == NULL){
|
|
ssh_set_error(session,SSH_FATAL,"Missing secret_hash");
|
|
return NULL;
|
|
}
|
|
|
|
if (privkey->type == SSH_KEYTYPE_ECDSA) {
|
|
#ifdef HAVE_ECC
|
|
unsigned char ehash[EVP_DIGEST_LEN] = {0};
|
|
uint32_t elen;
|
|
|
|
evp(privkey->ecdsa_nid, crypto->secret_hash, crypto->digest_len,
|
|
ehash, &elen);
|
|
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("Hash being signed", ehash, elen);
|
|
#endif
|
|
|
|
sig = pki_do_sign_sessionid(privkey, ehash, elen);
|
|
if (sig == NULL) {
|
|
return NULL;
|
|
}
|
|
#endif
|
|
} else if (privkey->type == SSH_KEYTYPE_ED25519) {
|
|
sig = ssh_signature_new();
|
|
if (sig == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
sig->type = privkey->type;
|
|
sig->type_c = privkey->type_c;
|
|
|
|
rc = pki_ed25519_sign(privkey,
|
|
sig,
|
|
crypto->secret_hash,
|
|
crypto->digest_len);
|
|
if (rc != SSH_OK){
|
|
ssh_signature_free(sig);
|
|
sig = NULL;
|
|
}
|
|
} else {
|
|
unsigned char hash[SHA_DIGEST_LEN] = {0};
|
|
SHACTX ctx;
|
|
|
|
ctx = sha1_init();
|
|
if (ctx == NULL) {
|
|
return NULL;
|
|
}
|
|
sha1_update(ctx, crypto->secret_hash, crypto->digest_len);
|
|
sha1_final(hash, ctx);
|
|
|
|
#ifdef DEBUG_CRYPTO
|
|
ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
|
|
#endif
|
|
|
|
sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN);
|
|
if (sig == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
rc = ssh_pki_export_signature_blob(sig, &sig_blob);
|
|
ssh_signature_free(sig);
|
|
if (rc < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return sig_blob;
|
|
}
|
|
#endif /* WITH_SERVER */
|
|
|
|
/**
|
|
* @}
|
|
*/
|