Replaced system SQLite with SQLCipher to support encrypted database
This commit is contained in:
344
Sources/DataLiteC/libtomcrypt/misc/pem/pem.c
Normal file
344
Sources/DataLiteC/libtomcrypt/misc/pem/pem.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#include "tomcrypt_private.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
|
||||
/**
|
||||
@file pem.c
|
||||
Const declarations for PEM, Steffen Jaeckel
|
||||
*/
|
||||
|
||||
#ifdef LTC_PEM
|
||||
|
||||
const struct pem_header_id pem_std_headers[] = {
|
||||
{
|
||||
/* PKCS#8 encrypted */
|
||||
SET_CSTR(.start, "-----BEGIN ENCRYPTED PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END ENCRYPTED PRIVATE KEY-----"),
|
||||
.has_more_headers = no,
|
||||
.flags = pf_encrypted_pkcs8,
|
||||
},
|
||||
{
|
||||
/* PKCS#8 plain */
|
||||
SET_CSTR(.start, "-----BEGIN PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END PRIVATE KEY-----"),
|
||||
.has_more_headers = no,
|
||||
.flags = pf_pkcs8,
|
||||
},
|
||||
{
|
||||
/* X.509 Certificates */
|
||||
SET_CSTR(.start, "-----BEGIN CERTIFICATE-----"),
|
||||
SET_CSTR(.end, "-----END CERTIFICATE-----"),
|
||||
.has_more_headers = no,
|
||||
.flags = pf_x509,
|
||||
},
|
||||
{
|
||||
/* Regular (plain) public keys */
|
||||
SET_CSTR(.start, "-----BEGIN PUBLIC KEY-----"),
|
||||
SET_CSTR(.end, "-----END PUBLIC KEY-----"),
|
||||
.has_more_headers = no,
|
||||
.flags = pf_public,
|
||||
},
|
||||
{
|
||||
SET_CSTR(.start, "-----BEGIN RSA PUBLIC KEY-----"),
|
||||
SET_CSTR(.end, "-----END RSA PUBLIC KEY-----"),
|
||||
.has_more_headers = no,
|
||||
.pka = LTC_PKA_RSA,
|
||||
.flags = pf_public,
|
||||
},
|
||||
/* Regular plain or encrypted private keys */
|
||||
{
|
||||
SET_CSTR(.start, "-----BEGIN RSA PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END RSA PRIVATE KEY-----"),
|
||||
.has_more_headers = maybe,
|
||||
.pka = LTC_PKA_RSA,
|
||||
},
|
||||
{
|
||||
SET_CSTR(.start, "-----BEGIN EC PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END EC PRIVATE KEY-----"),
|
||||
.has_more_headers = maybe,
|
||||
.pka = LTC_PKA_EC,
|
||||
},
|
||||
{
|
||||
SET_CSTR(.start, "-----BEGIN DSA PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END DSA PRIVATE KEY-----"),
|
||||
.has_more_headers = maybe,
|
||||
.pka = LTC_PKA_DSA,
|
||||
},
|
||||
};
|
||||
const unsigned long pem_std_headers_num = sizeof(pem_std_headers)/sizeof(pem_std_headers[0]);
|
||||
|
||||
/* Encrypted PEM files */
|
||||
const struct str pem_proc_type_encrypted = { SET_CSTR(, "Proc-Type: 4,ENCRYPTED") };
|
||||
#if defined(LTC_SSH)
|
||||
const struct str pem_ssh_comment = { SET_CSTR(, "Comment: ") };
|
||||
#endif
|
||||
const struct str pem_dek_info_start = { SET_CSTR(, "DEK-Info: ") };
|
||||
const struct blockcipher_info pem_dek_infos[] =
|
||||
{
|
||||
{ .name = "AES-128-CBC,", .algo = "aes", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "AES-192-CBC,", .algo = "aes", .keylen = 192 / 8, .mode = cm_cbc, },
|
||||
{ .name = "AES-256-CBC,", .algo = "aes", .keylen = 256 / 8, .mode = cm_cbc, },
|
||||
{ .name = "AES-128-CFB,", .algo = "aes", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "AES-192-CFB,", .algo = "aes", .keylen = 192 / 8, .mode = cm_cfb, },
|
||||
{ .name = "AES-256-CFB,", .algo = "aes", .keylen = 256 / 8, .mode = cm_cfb, },
|
||||
{ .name = "AES-128-CFB1,", .algo = "aes", .keylen = 128 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "AES-192-CFB1,", .algo = "aes", .keylen = 192 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "AES-256-CFB1,", .algo = "aes", .keylen = 256 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "AES-128-CFB8,", .algo = "aes", .keylen = 128 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "AES-192-CFB8,", .algo = "aes", .keylen = 192 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "AES-256-CFB8,", .algo = "aes", .keylen = 256 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "AES-128-CTR,", .algo = "aes", .keylen = 128 / 8, .mode = cm_ctr, },
|
||||
{ .name = "AES-192-CTR,", .algo = "aes", .keylen = 192 / 8, .mode = cm_ctr, },
|
||||
{ .name = "AES-256-CTR,", .algo = "aes", .keylen = 256 / 8, .mode = cm_ctr, },
|
||||
{ .name = "AES-128-OFB,", .algo = "aes", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "AES-192-OFB,", .algo = "aes", .keylen = 192 / 8, .mode = cm_ofb, },
|
||||
{ .name = "AES-256-OFB,", .algo = "aes", .keylen = 256 / 8, .mode = cm_ofb, },
|
||||
{ .name = "BF-CBC,", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "BF-CFB,", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "BF-OFB,", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "CAMELLIA-128-CBC,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "CAMELLIA-192-CBC,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cbc, },
|
||||
{ .name = "CAMELLIA-256-CBC,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cbc, },
|
||||
{ .name = "CAMELLIA-128-CFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "CAMELLIA-192-CFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cfb, },
|
||||
{ .name = "CAMELLIA-256-CFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cfb, },
|
||||
{ .name = "CAMELLIA-128-CFB1,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "CAMELLIA-192-CFB1,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "CAMELLIA-256-CFB1,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "CAMELLIA-128-CFB8,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "CAMELLIA-192-CFB8,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "CAMELLIA-256-CFB8,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "CAMELLIA-128-CTR,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ctr, },
|
||||
{ .name = "CAMELLIA-192-CTR,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ctr, },
|
||||
{ .name = "CAMELLIA-256-CTR,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ctr, },
|
||||
{ .name = "CAMELLIA-128-OFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "CAMELLIA-192-OFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ofb, },
|
||||
{ .name = "CAMELLIA-256-OFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ofb, },
|
||||
{ .name = "CAST5-CBC,", .algo = "cast5", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "CAST5-CFB,", .algo = "cast5", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "CAST5-OFB,", .algo = "cast5", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "ChaCha20,", .algo = "chacha20", .keylen = 256 / 8, .mode = cm_stream, },
|
||||
{ .name = "DES-EDE-CBC,", .algo = "3des", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "DES-EDE-CFB,", .algo = "3des", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "DES-EDE-OFB,", .algo = "3des", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "DES-EDE3-CBC,", .algo = "3des", .keylen = 192 / 8, .mode = cm_cbc, },
|
||||
{ .name = "DES-EDE3-CFB,", .algo = "3des", .keylen = 192 / 8, .mode = cm_cfb, },
|
||||
{ .name = "DES-EDE3-CFB1,", .algo = "3des", .keylen = 192 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "DES-EDE3-CFB8,", .algo = "3des", .keylen = 192 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "DES-EDE3-OFB,", .algo = "3des", .keylen = 192 / 8, .mode = cm_ofb, },
|
||||
{ .name = "DES-CBC,", .algo = "des", .keylen = 64 / 8, .mode = cm_cbc, },
|
||||
{ .name = "DES-CFB,", .algo = "des", .keylen = 64 / 8, .mode = cm_cfb, },
|
||||
{ .name = "DES-CFB1,", .algo = "des", .keylen = 64 / 8, .mode = cm_cfb1, },
|
||||
{ .name = "DES-CFB8,", .algo = "des", .keylen = 64 / 8, .mode = cm_cfb8, },
|
||||
{ .name = "DES-OFB,", .algo = "des", .keylen = 64 / 8, .mode = cm_ofb, },
|
||||
{ .name = "DESX-CBC,", .algo = "desx", .keylen = 192 / 8, .mode = cm_cbc, },
|
||||
{ .name = "IDEA-CBC,", .algo = "idea", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "IDEA-CFB,", .algo = "idea", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "IDEA-OFB,", .algo = "idea", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "RC5-CBC,", .algo = "rc5", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "RC5-CFB,", .algo = "rc5", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "RC5-OFB,", .algo = "rc5", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "RC2-40-CBC,", .algo = "rc2", .keylen = 40 / 8, .mode = cm_cbc, },
|
||||
{ .name = "RC2-64-CBC,", .algo = "rc2", .keylen = 64 / 8, .mode = cm_cbc, },
|
||||
{ .name = "RC2-CBC,", .algo = "rc2", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "RC2-CFB,", .algo = "rc2", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "RC2-OFB,", .algo = "rc2", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
{ .name = "SEED-CBC,", .algo = "seed", .keylen = 128 / 8, .mode = cm_cbc, },
|
||||
{ .name = "SEED-CFB,", .algo = "seed", .keylen = 128 / 8, .mode = cm_cfb, },
|
||||
{ .name = "SEED-OFB,", .algo = "seed", .keylen = 128 / 8, .mode = cm_ofb, },
|
||||
};
|
||||
const unsigned long pem_dek_infos_num = sizeof(pem_dek_infos)/sizeof(pem_dek_infos[0]);
|
||||
|
||||
int pem_decrypt(unsigned char *data, unsigned long *datalen,
|
||||
unsigned char *key, unsigned long keylen,
|
||||
unsigned char *iv, unsigned long ivlen,
|
||||
unsigned char *tag, unsigned long taglen,
|
||||
const struct blockcipher_info *info,
|
||||
enum padding_type padding)
|
||||
{
|
||||
int err, cipher = -1;
|
||||
struct {
|
||||
union {
|
||||
#ifdef LTC_CBC_MODE
|
||||
symmetric_CBC cbc;
|
||||
#endif
|
||||
#ifdef LTC_CFB_MODE
|
||||
symmetric_CFB cfb;
|
||||
#endif
|
||||
#ifdef LTC_CTR_MODE
|
||||
symmetric_CTR ctr;
|
||||
#endif
|
||||
#ifdef LTC_OFB_MODE
|
||||
symmetric_OFB ofb;
|
||||
#endif
|
||||
} ctx;
|
||||
} s;
|
||||
enum cipher_mode mode = info->mode & cm_modes;
|
||||
|
||||
if (mode != cm_stream) {
|
||||
cipher = find_cipher(info->algo);
|
||||
if (cipher == -1) {
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
}
|
||||
}
|
||||
|
||||
switch (info->mode) {
|
||||
case cm_cbc:
|
||||
#ifdef LTC_CBC_MODE
|
||||
LTC_ARGCHK(ivlen == (unsigned long)cipher_descriptor[cipher].block_length);
|
||||
|
||||
if ((err = cbc_start(cipher, iv, key, keylen, 0, &s.ctx.cbc)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = cbc_decrypt(data, data, *datalen, &s.ctx.cbc)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = cbc_done(&s.ctx.cbc)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((err = padding_depad(data, datalen, padding | s.ctx.cbc.blocklen)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_cfb:
|
||||
case cm_cfb1:
|
||||
case cm_cfb8:
|
||||
#ifdef LTC_CFB_MODE
|
||||
if (info->mode == cm_cfb) {
|
||||
if ((err = cfb_start(cipher, iv, key, keylen, 0, &s.ctx.cfb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
} else {
|
||||
if ((err = cfb_start_ex(cipher, iv, key, keylen, 0, info->mode == cm_cfb1 ? 1 : 8, &s.ctx.cfb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
if ((err = cfb_decrypt(data, data, *datalen, &s.ctx.cfb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = cfb_done(&s.ctx.cfb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_ctr:
|
||||
#ifdef LTC_CTR_MODE
|
||||
if ((err = ctr_start(cipher, iv, key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &s.ctx.ctr)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = ctr_decrypt(data, data, *datalen, &s.ctx.ctr)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = ctr_done(&s.ctx.ctr)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_ofb:
|
||||
#ifdef LTC_OFB_MODE
|
||||
if ((err = ofb_start(cipher, iv, key, keylen, 0, &s.ctx.ofb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = ofb_decrypt(data, data, *datalen, &s.ctx.ofb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
if ((err = ofb_done(&s.ctx.ofb)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_gcm:
|
||||
#ifdef LTC_GCM_MODE
|
||||
if ((err = gcm_memory(cipher,
|
||||
key, keylen,
|
||||
iv, ivlen,
|
||||
NULL, 0,
|
||||
data, *datalen, data,
|
||||
tag, &taglen,
|
||||
GCM_DECRYPT)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
LTC_UNUSED_PARAM(tag);
|
||||
LTC_UNUSED_PARAM(taglen);
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_stream:
|
||||
#ifdef LTC_CHACHA
|
||||
LTC_ARGCHK(ivlen == 16);
|
||||
|
||||
if ((err = chacha_memory(key, keylen, 20,
|
||||
iv, ivlen, 0,
|
||||
data, *datalen, data)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
case cm_stream_openssh:
|
||||
#ifdef LTC_CHACHA20POLY1305_MODE
|
||||
if ((err = chacha20poly1305_memory(key, 32,
|
||||
iv, ivlen,
|
||||
NULL, 0,
|
||||
data, *datalen, data,
|
||||
tag, &taglen,
|
||||
CHACHA20POLY1305_DECRYPT | CHACHA20POLY1305_OPENSSH_COMPAT)) != CRYPT_OK) {
|
||||
goto error_out;
|
||||
}
|
||||
#else
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
err = CRYPT_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
|
||||
error_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef LTC_NO_FILE
|
||||
int pem_decode_filehandle(FILE *f, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
int err = pem_decode_pkcs_filehandle(f, k, pw_ctx);
|
||||
if (err == CRYPT_OK || err != CRYPT_UNKNOWN_PEM)
|
||||
return err;
|
||||
#if defined(LTC_SSH)
|
||||
rewind(f);
|
||||
err = pem_decode_openssh_filehandle(f, k, pw_ctx);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
int pem_decode(const void *buf, unsigned long len, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
int err = pem_decode_pkcs(buf, len, k, pw_ctx);
|
||||
if (err == CRYPT_OK || err != CRYPT_UNKNOWN_PEM)
|
||||
return err;
|
||||
#if defined(LTC_SSH)
|
||||
err = pem_decode_openssh(buf, len, k, pw_ctx);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* LTC_PEM */
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
298
Sources/DataLiteC/libtomcrypt/misc/pem/pem_pkcs.c
Normal file
298
Sources/DataLiteC/libtomcrypt/misc/pem/pem_pkcs.c
Normal file
@@ -0,0 +1,298 @@
|
||||
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#include "tomcrypt_private.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
|
||||
/**
|
||||
@file pem_decode.c
|
||||
Decode a PEM file, Steffen Jaeckel
|
||||
*/
|
||||
|
||||
#ifdef LTC_PEM
|
||||
|
||||
extern const struct pem_header_id pem_std_headers[];
|
||||
extern const unsigned long pem_std_headers_num;
|
||||
|
||||
static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_headers *hdr)
|
||||
{
|
||||
unsigned char iv[MAXBLOCKSIZE], key[MAXBLOCKSIZE];
|
||||
unsigned long ivlen, klen;
|
||||
int err;
|
||||
|
||||
if (hdr->info.keylen > sizeof(key)) {
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
if (!hdr->pw->pw) {
|
||||
return CRYPT_INVALID_ARG;
|
||||
}
|
||||
|
||||
ivlen = sizeof(iv);
|
||||
if ((err = base16_decode(hdr->info.iv, XSTRLEN(hdr->info.iv), iv, &ivlen)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
klen = hdr->info.keylen;
|
||||
if ((err = pkcs_5_alg1_openssl(hdr->pw->pw, hdr->pw->l, iv, 1, find_hash("md5"), key, &klen))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pem_decrypt(pem, l, key, klen, iv, ivlen, NULL, 0, &hdr->info, LTC_PAD_PKCS7);
|
||||
|
||||
zeromem(key, sizeof(key));
|
||||
zeromem(iv, sizeof(iv));
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s_get_pka(ltc_asn1_list *pub, enum ltc_pka_id *pka)
|
||||
{
|
||||
der_flexi_check flexi_should[4];
|
||||
ltc_asn1_list *seqid, *id;
|
||||
enum ltc_oid_id oid_id;
|
||||
int err;
|
||||
unsigned long n = 0;
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid);
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_BIT_STRING, NULL);
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
|
||||
if ((err = der_flexi_sequence_cmp(pub, flexi_should)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
n = 0;
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_OBJECT_IDENTIFIER, &id);
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
|
||||
err = der_flexi_sequence_cmp(seqid, flexi_should);
|
||||
if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
|
||||
return err;
|
||||
}
|
||||
if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
return pk_get_pka_id(oid_id, pka);
|
||||
}
|
||||
|
||||
typedef int (*import_fn)(const unsigned char *, unsigned long, void*);
|
||||
|
||||
static const import_fn s_import_x509_fns[LTC_PKA_NUM] = {
|
||||
#ifdef LTC_MRSA
|
||||
[LTC_PKA_RSA] = (import_fn)rsa_import_x509,
|
||||
#endif
|
||||
#ifdef LTC_MECC
|
||||
[LTC_PKA_EC] = (import_fn)ecc_import_x509,
|
||||
#endif
|
||||
#ifdef LTC_CURVE25519
|
||||
[LTC_PKA_X25519] = (import_fn)x25519_import_x509,
|
||||
[LTC_PKA_ED25519] = (import_fn)ed25519_import_x509,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int s_import_x509(unsigned char *pem, unsigned long l, ltc_pka_key *k)
|
||||
{
|
||||
enum ltc_pka_id pka = LTC_PKA_UNDEF;
|
||||
ltc_asn1_list *d, *spki;
|
||||
int err;
|
||||
if ((err = x509_decode_spki(pem, l, &d, &spki)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
err = s_get_pka(spki, &pka);
|
||||
der_free_sequence_flexi(d);
|
||||
if (err != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (pka < 0
|
||||
|| pka > sizeof(s_import_x509_fns)/sizeof(s_import_x509_fns[0])
|
||||
|| s_import_x509_fns[pka] == NULL) {
|
||||
return CRYPT_PK_INVALID_TYPE;
|
||||
}
|
||||
if ((err = s_import_x509_fns[pka](pem, l, &k->u)) == CRYPT_OK) {
|
||||
k->id = pka;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s_import_pkcs8(unsigned char *pem, unsigned long l, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
int err;
|
||||
enum ltc_oid_id pka;
|
||||
ltc_asn1_list *alg_id, *priv_key;
|
||||
ltc_asn1_list *p8_asn1 = NULL;
|
||||
if ((err = pkcs8_decode_flexi(pem, l, pw_ctx, &p8_asn1)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
if ((err = pkcs8_get_children(p8_asn1, &pka, &alg_id, &priv_key)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
switch (pka) {
|
||||
#ifdef LTC_MDH
|
||||
case LTC_OID_DH:
|
||||
err = dh_import_pkcs8_asn1(alg_id, priv_key, &k->u.dh);
|
||||
k->id = LTC_PKA_DH;
|
||||
break;
|
||||
#endif
|
||||
#ifdef LTC_MDSA
|
||||
case LTC_OID_DSA:
|
||||
err = dsa_import_pkcs8_asn1(alg_id, priv_key, &k->u.dsa);
|
||||
k->id = LTC_PKA_DSA;
|
||||
break;
|
||||
#endif
|
||||
#ifdef LTC_MRSA
|
||||
case LTC_OID_RSA:
|
||||
err = rsa_import_pkcs8_asn1(alg_id, priv_key, &k->u.rsa);
|
||||
k->id = LTC_PKA_RSA;
|
||||
break;
|
||||
#endif
|
||||
#ifdef LTC_MECC
|
||||
case LTC_OID_EC:
|
||||
err = ecc_import_pkcs8_asn1(alg_id, priv_key, &k->u.ecc);
|
||||
k->id = LTC_PKA_EC;
|
||||
break;
|
||||
#endif
|
||||
#ifdef LTC_CURVE25519
|
||||
case LTC_OID_X25519:
|
||||
err = x25519_import_pkcs8_asn1(alg_id, priv_key, &k->u.x25519);
|
||||
k->id = LTC_PKA_X25519;
|
||||
break;
|
||||
case LTC_OID_ED25519:
|
||||
err = ed25519_import_pkcs8_asn1(alg_id, priv_key, &k->u.ed25519);
|
||||
k->id = LTC_PKA_ED25519;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
err = CRYPT_PK_INVALID_TYPE;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (p8_asn1) {
|
||||
der_sequence_free(p8_asn1);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s_extract_pka(unsigned char *pem, unsigned long w, enum ltc_pka_id *pka)
|
||||
{
|
||||
ltc_asn1_list *pub;
|
||||
int err = CRYPT_ERROR;
|
||||
if ((err = der_decode_sequence_flexi(pem, &w, &pub)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
err = s_get_pka(pub, pka);
|
||||
der_sequence_free(pub);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const import_fn s_import_openssl_fns[LTC_PKA_NUM] = {
|
||||
#ifdef LTC_MRSA
|
||||
[LTC_PKA_RSA] = (import_fn)rsa_import,
|
||||
#endif
|
||||
#ifdef LTC_MDSA
|
||||
[LTC_PKA_DSA] = (import_fn)dsa_import,
|
||||
#endif
|
||||
#ifdef LTC_MECC
|
||||
[LTC_PKA_EC] = (import_fn)ecc_import_openssl,
|
||||
#endif
|
||||
#ifdef LTC_CURVE25519
|
||||
[LTC_PKA_X25519] = (import_fn)x25519_import,
|
||||
[LTC_PKA_ED25519] = (import_fn)ed25519_import,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
unsigned char *pem = NULL;
|
||||
unsigned long w, l, n;
|
||||
int err = CRYPT_ERROR;
|
||||
struct pem_headers hdr = { 0 };
|
||||
struct password pw = { 0 };
|
||||
enum ltc_pka_id pka;
|
||||
XMEMSET(k, 0, sizeof(*k));
|
||||
w = LTC_PEM_READ_BUFSIZE * 2;
|
||||
retry:
|
||||
pem = XREALLOC(pem, w);
|
||||
for (n = 0; n < pem_std_headers_num; ++n) {
|
||||
hdr.id = &pem_std_headers[n];
|
||||
err = pem_read(pem, &w, &hdr, g);
|
||||
if (err == CRYPT_BUFFER_OVERFLOW) {
|
||||
goto retry;
|
||||
} else if (err == CRYPT_OK) {
|
||||
break;
|
||||
} else if (err != CRYPT_UNKNOWN_PEM) {
|
||||
goto cleanup;
|
||||
}
|
||||
hdr.id = NULL;
|
||||
}
|
||||
/* id not found */
|
||||
if (hdr.id == NULL)
|
||||
goto cleanup;
|
||||
l = w;
|
||||
if (hdr.id->flags & pf_pkcs8) {
|
||||
err = s_import_pkcs8(pem, l, k, pw_ctx);
|
||||
goto cleanup;
|
||||
} else if (hdr.id->flags == pf_x509) {
|
||||
err = s_import_x509(pem, l, k);
|
||||
goto cleanup;
|
||||
} else if ((hdr.id->flags & pf_public) && hdr.id->pka == LTC_PKA_UNDEF) {
|
||||
if ((err = s_extract_pka(pem, w, &pka)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (hdr.encrypted) {
|
||||
if ((pw_ctx == NULL) || (pw_ctx->callback == NULL)) {
|
||||
err = CRYPT_PW_CTX_MISSING;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hdr.pw = &pw;
|
||||
if (pw_ctx->callback(&hdr.pw->pw, &hdr.pw->l, pw_ctx->userdata)) {
|
||||
err = CRYPT_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((err = s_decrypt_pem(pem, &l, &hdr)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
pka = hdr.id->pka;
|
||||
} else {
|
||||
pka = hdr.id->pka;
|
||||
}
|
||||
|
||||
if (pka < 0
|
||||
|| pka > sizeof(s_import_openssl_fns)/sizeof(s_import_openssl_fns[0])
|
||||
|| s_import_openssl_fns[pka] == NULL) {
|
||||
err = CRYPT_PK_INVALID_TYPE;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((err = s_import_openssl_fns[pka](pem, l, &k->u)) == CRYPT_OK) {
|
||||
k->id = pka;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
password_free(hdr.pw, pw_ctx);
|
||||
XFREE(pem);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef LTC_NO_FILE
|
||||
int pem_decode_pkcs_filehandle(FILE *f, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
LTC_ARGCHK(f != NULL);
|
||||
LTC_ARGCHK(k != NULL);
|
||||
{
|
||||
struct get_char g = { .get = pem_get_char_from_file, .data.f = f };
|
||||
return s_decode(&g, k, pw_ctx);
|
||||
}
|
||||
}
|
||||
#endif /* LTC_NO_FILE */
|
||||
|
||||
int pem_decode_pkcs(const void *buf, unsigned long len, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
LTC_ARGCHK(buf != NULL);
|
||||
LTC_ARGCHK(len != 0);
|
||||
LTC_ARGCHK(k != NULL);
|
||||
{
|
||||
struct get_char g = { .get = pem_get_char_from_buf, SET_BUFP(.data.buf, buf, len) };
|
||||
return s_decode(&g, k, pw_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LTC_PEM */
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
244
Sources/DataLiteC/libtomcrypt/misc/pem/pem_read.c
Normal file
244
Sources/DataLiteC/libtomcrypt/misc/pem/pem_read.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#include "tomcrypt_private.h"
|
||||
|
||||
/**
|
||||
@file pem_read.c
|
||||
Read and interpret a PEM file, Steffen Jaeckel
|
||||
*/
|
||||
|
||||
#ifdef LTC_PEM
|
||||
|
||||
extern const struct str pem_proc_type_encrypted;
|
||||
#ifdef LTC_SSH
|
||||
extern const struct str pem_ssh_comment;
|
||||
#endif
|
||||
extern const struct str pem_dek_info_start;
|
||||
extern const struct blockcipher_info pem_dek_infos[];
|
||||
extern const unsigned long pem_dek_infos_num;
|
||||
|
||||
#ifndef LTC_NO_FILE
|
||||
int pem_get_char_from_file(struct get_char *g)
|
||||
{
|
||||
return getc(g->data.f);
|
||||
}
|
||||
#endif /* LTC_NO_FILE */
|
||||
|
||||
int pem_get_char_from_buf(struct get_char *g)
|
||||
{
|
||||
int ret;
|
||||
if (g->data.buf.work == g->data.buf.end) {
|
||||
return -1;
|
||||
}
|
||||
ret = *g->data.buf.work;
|
||||
g->data.buf.work++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void s_unget_line(char *buf, unsigned long buflen, struct get_char *g)
|
||||
{
|
||||
if (buflen > sizeof(g->unget_buf_))
|
||||
return;
|
||||
g->unget_buf.p = g->unget_buf_;
|
||||
COPY_STR(g->unget_buf, buf, buflen);
|
||||
}
|
||||
|
||||
static void s_tts(char *buf, unsigned long *buflen)
|
||||
{
|
||||
while(1) {
|
||||
unsigned long blen = *buflen;
|
||||
if (blen < 2)
|
||||
return;
|
||||
blen--;
|
||||
switch (buf[blen]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
buf[blen] = '\0';
|
||||
*buflen = blen;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char* s_get_line(char *buf, unsigned long *buflen, struct get_char *g)
|
||||
{
|
||||
unsigned long blen = 0;
|
||||
int c = -1, c_;
|
||||
if (g->unget_buf.p) {
|
||||
if (*buflen < g->unget_buf.len) {
|
||||
return NULL;
|
||||
}
|
||||
XMEMCPY(buf, g->unget_buf.p, g->unget_buf.len);
|
||||
*buflen = g->unget_buf.len;
|
||||
RESET_STR(g->unget_buf);
|
||||
return buf;
|
||||
}
|
||||
while(blen < *buflen) {
|
||||
c_ = c;
|
||||
c = g->get(g);
|
||||
if (c == '\n') {
|
||||
buf[blen] = '\0';
|
||||
if (c_ == '\r') {
|
||||
buf[--blen] = '\0';
|
||||
}
|
||||
s_tts(buf, &blen);
|
||||
*buflen = blen;
|
||||
return buf;
|
||||
}
|
||||
if (c == -1 || c == '\0') {
|
||||
buf[blen] = '\0';
|
||||
s_tts(buf, &blen);
|
||||
*buflen = blen;
|
||||
return buf;
|
||||
}
|
||||
buf[blen] = c;
|
||||
blen++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static LTC_INLINE int s_fits_buf(void *dest, unsigned long to_write, void *end)
|
||||
{
|
||||
unsigned char *d = dest;
|
||||
unsigned char *e = end;
|
||||
unsigned char *w = d + to_write;
|
||||
if (w < d || w > e)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int s_pem_decode_headers(struct pem_headers *hdr, struct get_char *g)
|
||||
{
|
||||
char buf[LTC_PEM_DECODE_BUFSZ], *alg_start;
|
||||
unsigned long slen, tmplen, n;
|
||||
int has_more_headers = hdr->id->has_more_headers == no ? 0 : 3;
|
||||
|
||||
/* Make sure the PEM has the appropriate extension headers if required.
|
||||
*
|
||||
* ```
|
||||
* Proc-Type: 4,ENCRYPTED[\r]\n
|
||||
* DEK-Info: <algorithm>,<IV>[\r]\n
|
||||
* [\r]\n
|
||||
* ```
|
||||
*/
|
||||
while (has_more_headers) {
|
||||
slen = sizeof(buf);
|
||||
if (!s_get_line(buf, &slen, g) || (has_more_headers > 1 && slen == 0)) {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
switch (has_more_headers) {
|
||||
case 3:
|
||||
if (XMEMCMP(buf, pem_proc_type_encrypted.p, pem_proc_type_encrypted.len)) {
|
||||
#ifdef LTC_SSH
|
||||
if (XMEMCMP(buf, pem_ssh_comment.p, pem_ssh_comment.len))
|
||||
#endif
|
||||
s_unget_line(buf, slen, g);
|
||||
if (hdr->id->has_more_headers == maybe)
|
||||
return CRYPT_OK;
|
||||
else
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
hdr->encrypted = 1;
|
||||
break;
|
||||
case 2:
|
||||
hdr->info.algo = NULL;
|
||||
if (XMEMCMP(buf, pem_dek_info_start.p, pem_dek_info_start.len))
|
||||
return CRYPT_INVALID_PACKET;
|
||||
alg_start = &buf[pem_dek_info_start.len];
|
||||
for (n = 0; n < pem_dek_infos_num; ++n) {
|
||||
unsigned long namelen = XSTRLEN(pem_dek_infos[n].name);
|
||||
if (slen >= namelen + pem_dek_info_start.len && !XMEMCMP(alg_start, pem_dek_infos[n].name, namelen)) {
|
||||
char *iv = alg_start + namelen;
|
||||
hdr->info = pem_dek_infos[n];
|
||||
tmplen = XSTRLEN(iv);
|
||||
if (tmplen > sizeof(hdr->info.iv))
|
||||
return CRYPT_INVALID_KEYSIZE;
|
||||
XMEMCPY(hdr->info.iv, iv, tmplen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hdr->info.algo == NULL) {
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Make sure that there's an empty line in between */
|
||||
if (buf[0] != '\0')
|
||||
return CRYPT_INVALID_PACKET;
|
||||
break;
|
||||
default:
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
}
|
||||
has_more_headers--;
|
||||
}
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
int pem_read(void *pem, unsigned long *w, struct pem_headers *hdr, struct get_char *g)
|
||||
{
|
||||
char buf[LTC_PEM_DECODE_BUFSZ];
|
||||
char *wpem = pem;
|
||||
char *end = wpem + *w;
|
||||
unsigned long slen, linelen;
|
||||
int err, hdr_ok = 0;
|
||||
int would_overflow = 0;
|
||||
unsigned char empty_lines = 0;
|
||||
|
||||
linelen = sizeof(buf);
|
||||
if (s_get_line(buf, &linelen, g) == NULL) {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
if (hdr->id->start.len != linelen || XMEMCMP(buf, hdr->id->start.p, hdr->id->start.len)) {
|
||||
s_unget_line(buf, linelen, g);
|
||||
return CRYPT_UNKNOWN_PEM;
|
||||
}
|
||||
|
||||
hdr->encrypted = hdr->id->flags & pf_encrypted;
|
||||
if ((err = s_pem_decode_headers(hdr, g)) != CRYPT_OK)
|
||||
return err;
|
||||
|
||||
/* Read the base64 encoded part of the PEM */
|
||||
slen = sizeof(buf);
|
||||
while (s_get_line(buf, &slen, g)) {
|
||||
if (slen == hdr->id->end.len && !XMEMCMP(buf, hdr->id->end.p, slen)) {
|
||||
hdr_ok = 1;
|
||||
break;
|
||||
}
|
||||
if (!slen) {
|
||||
if (empty_lines)
|
||||
break;
|
||||
empty_lines++;
|
||||
}
|
||||
if (!would_overflow && s_fits_buf(wpem, slen, end)) {
|
||||
XMEMCPY(wpem, buf, slen);
|
||||
} else {
|
||||
would_overflow = 1;
|
||||
}
|
||||
wpem += slen;
|
||||
slen = sizeof(buf);
|
||||
}
|
||||
if (!hdr_ok)
|
||||
return CRYPT_INVALID_PACKET;
|
||||
|
||||
if (would_overflow || !s_fits_buf(wpem, 1, end)) {
|
||||
/* NUL termination */
|
||||
wpem++;
|
||||
/* prevent a wrap-around */
|
||||
if (wpem < (char*)pem)
|
||||
return CRYPT_OVERFLOW;
|
||||
*w = wpem - (char*)pem;
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
*w = wpem - (char*)pem;
|
||||
*wpem++ = '\0';
|
||||
|
||||
if ((err = base64_strict_decode(pem, *w, pem, w)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
#endif /* LTC_PEM */
|
||||
861
Sources/DataLiteC/libtomcrypt/misc/pem/pem_ssh.c
Normal file
861
Sources/DataLiteC/libtomcrypt/misc/pem/pem_ssh.c
Normal file
@@ -0,0 +1,861 @@
|
||||
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#include "tomcrypt_private.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
|
||||
/**
|
||||
@file pem_ssh.c
|
||||
SSH specific functionality to process PEM files, Steffen Jaeckel
|
||||
|
||||
The basic format of the key is described here:
|
||||
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||
*/
|
||||
|
||||
#if defined(LTC_PEM_SSH)
|
||||
|
||||
/* Table as of
|
||||
* https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml#ssh-parameters-17
|
||||
*/
|
||||
const struct blockcipher_info ssh_ciphers[] =
|
||||
{
|
||||
{ .name = "none", .algo = "", .keylen = 0, .mode = cm_none },
|
||||
{ .name = "aes128-cbc", .algo = "aes", .keylen = 128 / 8, .mode = cm_cbc },
|
||||
{ .name = "aes128-ctr", .algo = "aes", .keylen = 128 / 8, .mode = cm_ctr },
|
||||
{ .name = "aes192-cbc", .algo = "aes", .keylen = 192 / 8, .mode = cm_cbc },
|
||||
{ .name = "aes192-ctr", .algo = "aes", .keylen = 192 / 8, .mode = cm_ctr },
|
||||
{ .name = "aes256-cbc", .algo = "aes", .keylen = 256 / 8, .mode = cm_cbc },
|
||||
{ .name = "aes256-ctr", .algo = "aes", .keylen = 256 / 8, .mode = cm_ctr },
|
||||
{ .name = "aes128-gcm@openssh.com", .algo = "aes", .keylen = 128 / 8, .mode = cm_gcm },
|
||||
{ .name = "aes256-gcm@openssh.com", .algo = "aes", .keylen = 256 / 8, .mode = cm_gcm },
|
||||
{ .name = "blowfish128-cbc", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc },
|
||||
{ .name = "blowfish128-ctr", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ctr },
|
||||
/* The algo name doesn't matter, it's only used in pem-info */
|
||||
{ .name = "chacha20-poly1305@openssh.com", .algo = "c20p1305", .keylen = 256 / 8, .mode = cm_stream | cm_openssh },
|
||||
{ .name = "des-cbc", .algo = "des", .keylen = 64 / 8, .mode = cm_cbc },
|
||||
{ .name = "3des-cbc", .algo = "3des", .keylen = 192 / 8, .mode = cm_cbc },
|
||||
{ .name = "3des-ctr", .algo = "3des", .keylen = 192 / 8, .mode = cm_ctr },
|
||||
{ .name = "serpent128-cbc", .algo = "serpent", .keylen = 128 / 8, .mode = cm_cbc },
|
||||
{ .name = "serpent128-ctr", .algo = "serpent", .keylen = 128 / 8, .mode = cm_ctr },
|
||||
{ .name = "serpent192-cbc", .algo = "serpent", .keylen = 192 / 8, .mode = cm_cbc },
|
||||
{ .name = "serpent192-ctr", .algo = "serpent", .keylen = 192 / 8, .mode = cm_ctr },
|
||||
{ .name = "serpent256-cbc", .algo = "serpent", .keylen = 256 / 8, .mode = cm_cbc },
|
||||
{ .name = "serpent256-ctr", .algo = "serpent", .keylen = 256 / 8, .mode = cm_ctr },
|
||||
{ .name = "twofish128-cbc", .algo = "twofish", .keylen = 128 / 8, .mode = cm_cbc },
|
||||
{ .name = "twofish128-ctr", .algo = "twofish", .keylen = 128 / 8, .mode = cm_ctr },
|
||||
{ .name = "twofish192-cbc", .algo = "twofish", .keylen = 192 / 8, .mode = cm_cbc },
|
||||
{ .name = "twofish192-ctr", .algo = "twofish", .keylen = 192 / 8, .mode = cm_ctr },
|
||||
{ .name = "twofish-cbc", .algo = "twofish", .keylen = 256 / 8, .mode = cm_cbc },
|
||||
{ .name = "twofish256-cbc", .algo = "twofish", .keylen = 256 / 8, .mode = cm_cbc },
|
||||
{ .name = "twofish256-ctr", .algo = "twofish", .keylen = 256 / 8, .mode = cm_ctr },
|
||||
};
|
||||
const unsigned long ssh_ciphers_num = sizeof(ssh_ciphers)/sizeof(ssh_ciphers[0]);
|
||||
|
||||
struct kdf_options {
|
||||
const char *name;
|
||||
const struct blockcipher_info *cipher;
|
||||
unsigned char salt[64];
|
||||
unsigned long saltlen;
|
||||
ulong32 num_rounds;
|
||||
struct password pw;
|
||||
};
|
||||
|
||||
#ifdef LTC_MECC
|
||||
static int s_ssh_find_ecc(const char *pka, const ltc_ecc_curve **curve)
|
||||
{
|
||||
int err;
|
||||
const char* prefix = "ecdsa-sha2-";
|
||||
unsigned long prefixlen = XSTRLEN(prefix);
|
||||
if (strstr(pka, prefix) == NULL) return CRYPT_PK_INVALID_TYPE;
|
||||
if ((err = ecc_find_curve(pka + prefixlen, curve)) != CRYPT_OK) return err;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
static int s_ssh_find_init_ecc(const char *pka, ltc_pka_key *key)
|
||||
{
|
||||
int err;
|
||||
const ltc_ecc_curve *cu;
|
||||
if ((err = s_ssh_find_ecc(pka, &cu)) != CRYPT_OK) return err;
|
||||
return ecc_set_curve(cu, &key->u.ecc);
|
||||
}
|
||||
|
||||
static int s_ssh_decode_ecdsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *pka_key, enum pem_flags type)
|
||||
{
|
||||
int err;
|
||||
unsigned char groupname[64], buf0[512], buf1[512];
|
||||
unsigned long groupnamelen = sizeof(groupname), buf0len = sizeof(buf0), buf1len = sizeof(buf1);
|
||||
unsigned long remaining, cur_len, keylen;
|
||||
const unsigned char *p, *key;
|
||||
|
||||
p = in;
|
||||
cur_len = *inlen;
|
||||
remaining = *inlen;
|
||||
|
||||
err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_STRING, groupname, &groupnamelen,
|
||||
LTC_SSHDATA_STRING, buf0, &buf0len,
|
||||
LTC_SSHDATA_STRING, buf1, &buf1len,
|
||||
LTC_SSHDATA_EOL, LTC_NULL);
|
||||
if (err == CRYPT_OK) {
|
||||
key = buf1;
|
||||
keylen = buf1len;
|
||||
} else if (err == CRYPT_BUFFER_OVERFLOW && buf0len != sizeof(buf0) && buf1len == sizeof(buf1)) {
|
||||
key = buf0;
|
||||
keylen = buf0len;
|
||||
} else {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
err = ecc_set_key(key, keylen, type == pf_public ? PK_PUBLIC : PK_PRIVATE, &pka_key->u.ecc);
|
||||
|
||||
cleanup:
|
||||
zeromem(groupname, sizeof(groupname));
|
||||
zeromem(buf0, sizeof(buf0));
|
||||
zeromem(buf1, sizeof(buf1));
|
||||
if (err == CRYPT_OK) {
|
||||
pka_key->id = LTC_PKA_EC;
|
||||
*inlen -= remaining;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LTC_CURVE25519
|
||||
static int s_ssh_decode_ed25519(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
|
||||
{
|
||||
int err;
|
||||
unsigned char pubkey[64], privkey[96];
|
||||
unsigned long pubkeylen = sizeof(pubkey), privkeylen = sizeof(privkey);
|
||||
unsigned long remaining, cur_len;
|
||||
const unsigned char *p;
|
||||
|
||||
p = in;
|
||||
cur_len = *inlen;
|
||||
remaining = *inlen;
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_STRING, pubkey, &pubkeylen,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (type == pf_public) {
|
||||
if ((err = ed25519_import_raw(pubkey, pubkeylen, PK_PUBLIC, &key->u.ed25519)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
key->id = LTC_PKA_ED25519;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_STRING, privkey, &privkeylen,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
if ((err = ed25519_import_raw(privkey, privkeylen, PK_PRIVATE, &key->u.ed25519)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
key->id = LTC_PKA_ED25519;
|
||||
|
||||
cleanup:
|
||||
zeromem(pubkey, sizeof(pubkey));
|
||||
zeromem(privkey, sizeof(privkey));
|
||||
if (err == CRYPT_OK) {
|
||||
remaining -= cur_len;
|
||||
*inlen -= remaining;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LTC_MRSA
|
||||
static int s_ssh_decode_dsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
|
||||
{
|
||||
int err, stat;
|
||||
unsigned long remaining, cur_len;
|
||||
const unsigned char *p;
|
||||
if ((err = dsa_int_init(&key->u.dsa)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
p = in;
|
||||
cur_len = *inlen;
|
||||
remaining = *inlen;
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_MPINT, key->u.dsa.p,
|
||||
LTC_SSHDATA_MPINT, key->u.dsa.q,
|
||||
LTC_SSHDATA_MPINT, key->u.dsa.g,
|
||||
LTC_SSHDATA_MPINT, key->u.dsa.y,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
key->u.dsa.qord = ltc_mp_unsigned_bin_size(key->u.dsa.q);
|
||||
if ((err = dsa_int_validate_pqg(&key->u.dsa, &stat)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (stat == 0) {
|
||||
err = CRYPT_INVALID_PACKET;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (type == pf_public) {
|
||||
key->id = LTC_PKA_DSA;
|
||||
key->u.dsa.type = PK_PUBLIC;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_MPINT, key->u.dsa.x,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
key->id = LTC_PKA_DSA;
|
||||
key->u.dsa.type = PK_PRIVATE;
|
||||
|
||||
cleanup:
|
||||
if (err != CRYPT_OK) {
|
||||
dsa_free(&key->u.dsa);
|
||||
} else {
|
||||
remaining -= cur_len;
|
||||
*inlen -= remaining;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LTC_MRSA
|
||||
static int s_ssh_decode_rsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
|
||||
{
|
||||
int err;
|
||||
void *tmp1, *tmp2;
|
||||
unsigned long remaining, cur_len;
|
||||
const unsigned char *p;
|
||||
|
||||
if ((err = rsa_init(&key->u.rsa)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
p = in;
|
||||
cur_len = *inlen;
|
||||
remaining = *inlen;
|
||||
|
||||
/* ssh-rsa public and private keys contain `e` and `N` in a different order
|
||||
* public contains `e`, then `N`
|
||||
* private contains `N`, then `e`
|
||||
* change the order later on if we import a public key */
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.N,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.e,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
if (type == pf_public) {
|
||||
/* c.f. comment above */
|
||||
void *exch = key->u.rsa.N;
|
||||
key->u.rsa.N = key->u.rsa.e;
|
||||
key->u.rsa.e = exch;
|
||||
key->id = LTC_PKA_RSA;
|
||||
key->u.rsa.type = PK_PUBLIC;
|
||||
*inlen -= remaining;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((err = ltc_mp_init_multi(&tmp1, &tmp2, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.d,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.qP,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.p,
|
||||
LTC_SSHDATA_MPINT, key->u.rsa.q,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup_tmps;
|
||||
}
|
||||
|
||||
if ((err = ltc_mp_sub_d(key->u.rsa.p, 1, tmp1)) != CRYPT_OK) { goto cleanup_tmps; } /* tmp1 = q-1 */
|
||||
if ((err = ltc_mp_sub_d(key->u.rsa.q, 1, tmp2)) != CRYPT_OK) { goto cleanup_tmps; } /* tmp2 = p-1 */
|
||||
if ((err = ltc_mp_mod(key->u.rsa.d, tmp1, key->u.rsa.dP)) != CRYPT_OK) { goto cleanup_tmps; } /* dP = d mod p-1 */
|
||||
if ((err = ltc_mp_mod(key->u.rsa.d, tmp2, key->u.rsa.dQ)) != CRYPT_OK) { goto cleanup_tmps; } /* dQ = d mod q-1 */
|
||||
|
||||
key->id = LTC_PKA_RSA;
|
||||
key->u.rsa.type = PK_PRIVATE;
|
||||
|
||||
cleanup_tmps:
|
||||
ltc_mp_deinit_multi(tmp2, tmp1, LTC_NULL);
|
||||
cleanup:
|
||||
if (err != CRYPT_OK) {
|
||||
rsa_free(&key->u.rsa);
|
||||
} else {
|
||||
remaining -= cur_len;
|
||||
*inlen -= remaining;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ssh_pka {
|
||||
struct str name;
|
||||
enum ltc_pka_id id;
|
||||
int (*find)(const char*, const ltc_ecc_curve **);
|
||||
int (*init)(const char*, ltc_pka_key*);
|
||||
int (*decode)(const unsigned char*, unsigned long*, ltc_pka_key*, enum pem_flags);
|
||||
};
|
||||
|
||||
struct ssh_pka ssh_pkas[] = {
|
||||
#ifdef LTC_CURVE25519
|
||||
{ SET_CSTR(.name, "ssh-ed25519"),
|
||||
LTC_PKA_ED25519,
|
||||
NULL,
|
||||
NULL,
|
||||
s_ssh_decode_ed25519 },
|
||||
#endif
|
||||
#ifdef LTC_MRSA
|
||||
{ SET_CSTR(.name, "ssh-rsa"),
|
||||
LTC_PKA_RSA,
|
||||
NULL,
|
||||
NULL,
|
||||
s_ssh_decode_rsa },
|
||||
#endif
|
||||
#ifdef LTC_MDSA
|
||||
{ SET_CSTR(.name, "ssh-dss"),
|
||||
LTC_PKA_DSA,
|
||||
NULL,
|
||||
NULL,
|
||||
s_ssh_decode_dsa },
|
||||
#endif
|
||||
#ifdef LTC_MECC
|
||||
{ { NULL, 0 },
|
||||
LTC_PKA_EC,
|
||||
s_ssh_find_ecc,
|
||||
s_ssh_find_init_ecc,
|
||||
s_ssh_decode_ecdsa },
|
||||
#endif
|
||||
};
|
||||
|
||||
static int s_decode_key(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, char **comment, enum pem_flags type)
|
||||
{
|
||||
int err;
|
||||
ulong32 check1, check2;
|
||||
unsigned char pka[64];
|
||||
unsigned long pkalen = sizeof(pka);
|
||||
unsigned long remaining, cur_len;
|
||||
const unsigned char *p;
|
||||
unsigned long n;
|
||||
|
||||
LTC_ARGCHK(in != NULL);
|
||||
LTC_ARGCHK(inlen != NULL);
|
||||
LTC_ARGCHK(key != NULL);
|
||||
|
||||
p = in;
|
||||
cur_len = *inlen;
|
||||
remaining = *inlen;
|
||||
|
||||
if (type != pf_public) {
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_UINT32, &check1,
|
||||
LTC_SSHDATA_UINT32, &check2,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (check1 != check2) {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
}
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_STRING, pka, &pkalen,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
for (n = 0; n < sizeof(ssh_pkas)/sizeof(ssh_pkas[0]); ++n) {
|
||||
if (ssh_pkas[n].name.p != NULL) {
|
||||
if (pkalen != ssh_pkas[n].name.len
|
||||
|| XMEMCMP(pka, ssh_pkas[n].name.p, ssh_pkas[n].name.len) != 0) continue;
|
||||
} else {
|
||||
if ((ssh_pkas[n].init == NULL) ||
|
||||
(ssh_pkas[n].init((char*)pka, key) != CRYPT_OK)) continue;
|
||||
}
|
||||
if ((err = ssh_pkas[n].decode(p, &cur_len, key, type)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (n == sizeof(ssh_pkas)/sizeof(ssh_pkas[0])) {
|
||||
return CRYPT_PK_INVALID_TYPE;
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
cur_len = remaining;
|
||||
|
||||
if (cur_len != 0 && comment) {
|
||||
unsigned long commentlen = cur_len;
|
||||
char *c = XMALLOC(commentlen);
|
||||
if (c == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
||||
LTC_SSHDATA_STRING, c, &commentlen,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (commentlen == 0) {
|
||||
XFREE(c);
|
||||
} else {
|
||||
*comment = c;
|
||||
}
|
||||
}
|
||||
|
||||
p += cur_len;
|
||||
remaining -= cur_len;
|
||||
|
||||
return remaining ? padding_depad(p, &remaining, LTC_PAD_SSH) : CRYPT_OK;
|
||||
}
|
||||
|
||||
static LTC_INLINE void skip_spaces(char **r, unsigned long *l)
|
||||
{
|
||||
while(*l && (**r == ' ' || **r == '\t')) {
|
||||
(*r)++;
|
||||
(*l)--;
|
||||
}
|
||||
}
|
||||
|
||||
static LTC_INLINE void skip_chars(char **r, unsigned long *l)
|
||||
{
|
||||
while(*l && (**r != ' ' && **r != '\t')) {
|
||||
(*l)--;
|
||||
if (**r == '\n' || **r == '\r') {
|
||||
*l = 0;
|
||||
} else {
|
||||
(*r)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LTC_INLINE void skip_to_eol(char **r, unsigned long *l)
|
||||
{
|
||||
while(*l && (**r != '\n' && **r != '\r')) {
|
||||
(*l)--;
|
||||
(*r)++;
|
||||
}
|
||||
}
|
||||
|
||||
static int s_parse_line(char *line, unsigned long *len, ltc_pka_key *key, char **comment)
|
||||
{
|
||||
int err;
|
||||
unsigned long n, rlen, olen;
|
||||
enum authorized_keys_elements {
|
||||
ake_algo_name = 0,
|
||||
ake_b64_encoded_key = 1,
|
||||
ake_comment = 2
|
||||
};
|
||||
struct str elements[3] = { 0 };
|
||||
char *r = line;
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
rlen = *len;
|
||||
/* Chop up string into the three authorized_keys_elements */
|
||||
for (n = 0; n < sizeof(elements)/sizeof(elements[0]) && rlen; ++n) {
|
||||
skip_spaces(&r, &rlen);
|
||||
elements[n].p = r;
|
||||
if (n != 2)
|
||||
skip_chars(&r, &rlen);
|
||||
else
|
||||
skip_to_eol(&r, &rlen);
|
||||
elements[n].len = r - elements[n].p;
|
||||
*r = '\0';
|
||||
r++;
|
||||
}
|
||||
|
||||
for (n = 0; n < sizeof(ssh_pkas)/sizeof(ssh_pkas[0]); ++n) {
|
||||
if (ssh_pkas[n].name.p != NULL) {
|
||||
if (elements[ake_algo_name].len != ssh_pkas[n].name.len
|
||||
|| XMEMCMP(elements[ake_algo_name].p, ssh_pkas[n].name.p, ssh_pkas[n].name.len) != 0) continue;
|
||||
} else {
|
||||
if ((ssh_pkas[n].find == NULL) ||
|
||||
(ssh_pkas[n].find(elements[ake_algo_name].p, NULL) != CRYPT_OK)) continue;
|
||||
}
|
||||
olen = elements[ake_b64_encoded_key].len;
|
||||
buf = XMALLOC(olen);
|
||||
if (buf == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
if ((err = base64_strict_decode(elements[ake_b64_encoded_key].p, elements[ake_b64_encoded_key].len, buf, &olen)) == CRYPT_OK) {
|
||||
err = s_decode_key(buf, &olen, key, comment, pf_public);
|
||||
if (err == CRYPT_OK && key->id != ssh_pkas[n].id) {
|
||||
err = CRYPT_PK_INVALID_TYPE;
|
||||
}
|
||||
}
|
||||
XFREE(buf);
|
||||
|
||||
if (err == CRYPT_OK) {
|
||||
/* Only use the comment that was maybe in the text we just processed, in case when
|
||||
* there was no comment inside the SSH key.
|
||||
*/
|
||||
if (*comment == NULL && elements[ake_comment].p) {
|
||||
*comment = XMALLOC(elements[ake_comment].len + 1);
|
||||
if (*comment == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
XMEMCPY(*comment, elements[ake_comment].p, elements[ake_comment].len);
|
||||
(*comment)[elements[ake_comment].len] = '\0';
|
||||
}
|
||||
*len = r - line;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
}
|
||||
return CRYPT_PK_INVALID_TYPE;
|
||||
}
|
||||
|
||||
static int s_read_authorized_keys(const void *buf, unsigned long len, ssh_authorized_key_cb cb, void *ctx)
|
||||
{
|
||||
char *s;
|
||||
int err;
|
||||
unsigned long clen = len;
|
||||
ltc_pka_key *key = XCALLOC(1, sizeof(*key));
|
||||
char *comment = NULL;
|
||||
void *cpy = XMALLOC(len);
|
||||
if (key == NULL || cpy == NULL) {
|
||||
if (cpy)
|
||||
XFREE(cpy);
|
||||
if (key)
|
||||
XFREE(key);
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
XMEMCPY(cpy, buf, len);
|
||||
s = cpy;
|
||||
while (clen && (err = s_parse_line(s, &clen, key, &comment)) == CRYPT_OK) {
|
||||
if (cb(key, comment, ctx)) {
|
||||
break;
|
||||
}
|
||||
s += clen;
|
||||
len -= clen;
|
||||
clen = len;
|
||||
key = XCALLOC(1, sizeof(*key));
|
||||
if (key == NULL) {
|
||||
err = CRYPT_MEM;
|
||||
break;
|
||||
}
|
||||
if (comment) {
|
||||
XFREE(comment);
|
||||
comment = NULL;
|
||||
}
|
||||
}
|
||||
if (comment)
|
||||
XFREE(comment);
|
||||
if (cpy)
|
||||
XFREE(cpy);
|
||||
if (key)
|
||||
XFREE(key);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s_decrypt_private_keys(unsigned char *in, unsigned long *inlen,
|
||||
unsigned char *tag, unsigned long taglen,
|
||||
struct kdf_options *opts)
|
||||
{
|
||||
int err, cipher;
|
||||
unsigned long symkey_len, iv_len;
|
||||
unsigned char symkey[MAXBLOCKSIZE], *iv, iv_[8] = { 0 };
|
||||
enum cipher_mode mode = opts->cipher->mode & cm_modes;
|
||||
|
||||
LTC_ARGCHK(in != NULL);
|
||||
LTC_ARGCHK(inlen != NULL);
|
||||
LTC_ARGCHK(opts != NULL);
|
||||
|
||||
if (mode != cm_stream) {
|
||||
cipher = find_cipher(opts->cipher->algo);
|
||||
if (cipher == -1) {
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
}
|
||||
|
||||
iv = symkey + opts->cipher->keylen;
|
||||
iv_len = mode == cm_gcm ? 12 : cipher_descriptor[cipher].block_length;
|
||||
symkey_len = opts->cipher->keylen + iv_len;
|
||||
} else {
|
||||
iv = iv_;
|
||||
iv_len = sizeof(iv_);
|
||||
symkey_len = 64;
|
||||
}
|
||||
|
||||
if (sizeof(symkey) < symkey_len) {
|
||||
return CRYPT_OVERFLOW;
|
||||
}
|
||||
|
||||
if ((err = bcrypt_pbkdf_openbsd(opts->pw.pw, opts->pw.l, opts->salt, opts->saltlen,
|
||||
opts->num_rounds, find_hash("sha512"), symkey, &symkey_len)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
err = pem_decrypt(in, inlen,
|
||||
symkey, opts->cipher->keylen,
|
||||
iv, iv_len,
|
||||
tag, taglen,
|
||||
opts->cipher, LTC_PAD_SSH);
|
||||
zeromem(symkey, sizeof(symkey));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s_decode_header(unsigned char *in, unsigned long *inlen, struct kdf_options *opts)
|
||||
{
|
||||
int err;
|
||||
unsigned char ciphername[64], kdfname[64], kdfoptions[128], pubkey1[2048];
|
||||
unsigned long ciphernamelen = sizeof(ciphername), kdfnamelen = sizeof(kdfname);
|
||||
unsigned long kdfoptionslen = sizeof(kdfoptions), pubkey1len = sizeof(pubkey1);
|
||||
ulong32 num_keys;
|
||||
unsigned long i;
|
||||
|
||||
void *magic = strstr((const char*)in, "openssh-key-v1");
|
||||
unsigned long slen = XSTRLEN("openssh-key-v1");
|
||||
unsigned char *start = &in[slen + 1];
|
||||
unsigned long len = *inlen - slen - 1;
|
||||
|
||||
if (magic == NULL || magic != in) {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(start, &len,
|
||||
LTC_SSHDATA_STRING, ciphername, &ciphernamelen,
|
||||
LTC_SSHDATA_STRING, kdfname, &kdfnamelen,
|
||||
LTC_SSHDATA_STRING, kdfoptions, &kdfoptionslen,
|
||||
LTC_SSHDATA_UINT32, &num_keys,
|
||||
LTC_SSHDATA_STRING, pubkey1, &pubkey1len,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (num_keys != 1) {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
|
||||
*inlen = len + slen + 1;
|
||||
|
||||
for (i = 0; i < ssh_ciphers_num; ++i) {
|
||||
if (XSTRCMP((char*)ciphername, ssh_ciphers[i].name) == 0) {
|
||||
opts->cipher = &ssh_ciphers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (opts->cipher == NULL) {
|
||||
return CRYPT_INVALID_CIPHER;
|
||||
}
|
||||
|
||||
if (XSTRCMP((char*)kdfname, "none") == 0) {
|
||||
/* NOP */
|
||||
opts->name = "none";
|
||||
} else if (XSTRCMP((char*)kdfname, "bcrypt") == 0) {
|
||||
opts->name = "bcrypt";
|
||||
opts->saltlen = sizeof(opts->salt);
|
||||
len = kdfoptionslen;
|
||||
if ((err = ssh_decode_sequence_multi(kdfoptions, &len,
|
||||
LTC_SSHDATA_STRING, opts->salt, &opts->saltlen,
|
||||
LTC_SSHDATA_UINT32, &opts->num_rounds,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (len != kdfoptionslen) {
|
||||
return CRYPT_INPUT_TOO_LONG;
|
||||
}
|
||||
} else {
|
||||
return CRYPT_INVALID_PACKET;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static const struct pem_header_id pem_openssh[] = {
|
||||
{
|
||||
SET_CSTR(.start, "-----BEGIN OPENSSH PRIVATE KEY-----"),
|
||||
SET_CSTR(.end, "-----END OPENSSH PRIVATE KEY-----"),
|
||||
.has_more_headers = no
|
||||
},
|
||||
{
|
||||
SET_CSTR(.start, "---- BEGIN SSH2 PUBLIC KEY ----"),
|
||||
SET_CSTR(.end, "---- END SSH2 PUBLIC KEY ----"),
|
||||
.has_more_headers = maybe,
|
||||
.flags = pf_public
|
||||
},
|
||||
};
|
||||
static const unsigned long pem_openssh_num = sizeof(pem_openssh)/sizeof(pem_openssh[0]);
|
||||
|
||||
static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
unsigned char *pem = NULL, *p, *privkey = NULL, *tag;
|
||||
unsigned long n, w, l, privkey_len, taglen;
|
||||
int err;
|
||||
struct pem_headers hdr;
|
||||
struct kdf_options opts = { 0 };
|
||||
XMEMSET(k, 0, sizeof(*k));
|
||||
w = LTC_PEM_READ_BUFSIZE * 2;
|
||||
retry:
|
||||
pem = XREALLOC(pem, w);
|
||||
for (n = 0; n < pem_openssh_num; ++n) {
|
||||
hdr.id = &pem_openssh[n];
|
||||
err = pem_read(pem, &w, &hdr, g);
|
||||
if (err == CRYPT_BUFFER_OVERFLOW) {
|
||||
goto retry;
|
||||
} else if (err == CRYPT_OK) {
|
||||
break;
|
||||
} else if (err != CRYPT_UNKNOWN_PEM) {
|
||||
goto cleanup;
|
||||
}
|
||||
hdr.id = NULL;
|
||||
}
|
||||
/* id not found */
|
||||
if (hdr.id == NULL) {
|
||||
goto cleanup;
|
||||
}
|
||||
p = pem;
|
||||
l = w;
|
||||
if (hdr.id->flags != pf_public) {
|
||||
if ((err = s_decode_header(pem, &w, &opts)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
p = pem + w;
|
||||
l -= w;
|
||||
w = l;
|
||||
|
||||
privkey_len = l;
|
||||
privkey = XMALLOC(privkey_len);
|
||||
if (privkey == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
|
||||
if ((err = ssh_decode_sequence_multi(p, &w,
|
||||
LTC_SSHDATA_STRING, privkey, &privkey_len,
|
||||
LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (XSTRCMP(opts.name, "none") != 0) {
|
||||
if ((pw_ctx == NULL) || (pw_ctx->callback == NULL)) {
|
||||
err = CRYPT_PW_CTX_MISSING;
|
||||
goto cleanup;
|
||||
}
|
||||
if (pw_ctx->callback(&opts.pw.pw, &opts.pw.l, pw_ctx->userdata)) {
|
||||
err = CRYPT_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
tag = p + w;
|
||||
taglen = l - w;
|
||||
w = privkey_len;
|
||||
if ((err = s_decrypt_private_keys(privkey, &privkey_len,
|
||||
tag, taglen,
|
||||
&opts)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
zeromem(opts.pw.pw, opts.pw.l);
|
||||
}
|
||||
|
||||
p = privkey;
|
||||
w = privkey_len;
|
||||
}
|
||||
if ((err = s_decode_key(p, &w, k, NULL, hdr.id->flags)) != CRYPT_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
password_free(&opts.pw, pw_ctx);
|
||||
if (privkey) {
|
||||
zeromem(privkey, privkey_len);
|
||||
XFREE(privkey);
|
||||
}
|
||||
XFREE(pem);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef LTC_NO_FILE
|
||||
int pem_decode_openssh_filehandle(FILE *f, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
LTC_ARGCHK(f != NULL);
|
||||
LTC_ARGCHK(k != NULL);
|
||||
{
|
||||
struct get_char g = { .get = pem_get_char_from_file, .data.f = f };
|
||||
return s_decode_openssh(&g, k, pw_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int ssh_read_authorized_keys_filehandle(FILE *f, ssh_authorized_key_cb cb, void *ctx)
|
||||
{
|
||||
size_t tot_data;
|
||||
void *buf;
|
||||
int err;
|
||||
|
||||
LTC_ARGCHK(f != NULL);
|
||||
LTC_ARGCHK(cb != NULL);
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
tot_data = ftell(f);
|
||||
rewind(f);
|
||||
buf = XMALLOC(tot_data);
|
||||
if (buf == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
if (fread(buf, 1, tot_data, f) != tot_data) {
|
||||
err = CRYPT_ERROR;
|
||||
} else {
|
||||
err = s_read_authorized_keys(buf, tot_data, cb, ctx);
|
||||
}
|
||||
XFREE(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* LTC_NO_FILE */
|
||||
|
||||
int pem_decode_openssh(const void *buf, unsigned long len, ltc_pka_key *k, const password_ctx *pw_ctx)
|
||||
{
|
||||
LTC_ARGCHK(buf != NULL);
|
||||
LTC_ARGCHK(len != 0);
|
||||
LTC_ARGCHK(k != NULL);
|
||||
{
|
||||
struct get_char g = { .get = pem_get_char_from_buf, SET_BUFP(.data.buf, buf, len) };
|
||||
return s_decode_openssh(&g, k, pw_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int ssh_read_authorized_keys(const void *buf, unsigned long len, ssh_authorized_key_cb cb, void *ctx)
|
||||
{
|
||||
LTC_ARGCHK(buf != NULL);
|
||||
LTC_ARGCHK(len != 0);
|
||||
LTC_ARGCHK(cb != NULL);
|
||||
|
||||
return s_read_authorized_keys(buf, len, cb, ctx);
|
||||
}
|
||||
#endif /* defined(LTC_PEM_SSH) */
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
Reference in New Issue
Block a user