Files
data-lite-c/Sources/DataLiteC/libtomcrypt/misc/pem/pem.c
2025-10-24 19:33:21 +03:00

345 lines
15 KiB
C

/* 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 = LTC_ARRAY_SIZE(pem_std_headers);
/* 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 = LTC_ARRAY_SIZE(pem_dek_infos);
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.ecb.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