Skip to content

Commit

Permalink
Add support for more algos of encrypted PEM files
Browse files Browse the repository at this point in the history
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
  • Loading branch information
sjaeckel committed Oct 12, 2023
1 parent f1e5f4c commit 48bdd0f
Show file tree
Hide file tree
Showing 101 changed files with 1,775 additions and 253 deletions.
23 changes: 15 additions & 8 deletions src/headers/tomcrypt_private.h
Expand Up @@ -255,16 +255,23 @@ int base64_encode_pem(const unsigned char *in, unsigned long inlen,
/* PEM related */

#ifdef LTC_PEM
enum cipher_mode {
cm_none, cm_cbc, cm_cfb, cm_ctr, cm_ofb, cm_stream, cm_gcm
};

struct password {
/* usually a `char*` but could also contain binary data
* so use a `void*` + length to be on the safe side.
*/
void *pw;
unsigned long l;
};
struct dek_info {
const char *alg;

struct blockcipher_info {
const char *name;
const char *algo;
unsigned long keylen;
enum cipher_mode mode;
/* should use `MAXBLOCKSIZE` here, but all supported
* blockciphers require max 16 bytes IV */
char iv[16 * 2 + 1];
Expand All @@ -280,11 +287,6 @@ struct str {
#define COPY_STR(n, s, l) do { XMEMCPY(n.p, s, l); n.len = l; } while(0)
#define RESET_STR(n) do { n.p = NULL; n.len = 0; } while(0)

struct dek_info_from_str {
const struct str id;
struct dek_info info;
};

enum more_headers {
no,
yes,
Expand All @@ -303,7 +305,7 @@ struct pem_header_id {
struct pem_headers {
const struct pem_header_id *id;
int encrypted;
struct dek_info info;
struct blockcipher_info info;
struct password *pw;
};

Expand Down Expand Up @@ -338,6 +340,11 @@ int pbes_decrypt(const pbes_arg *arg, unsigned char *dec_data, unsigned long *d
int pbes1_extract(const ltc_asn1_list *s, pbes_arg *res);
int pbes2_extract(const ltc_asn1_list *s, pbes_arg *res);

int pem_decrypt(unsigned char *data, unsigned long *datalen,
unsigned char *key, unsigned long keylen,
unsigned char *iv, unsigned long ivlen,
const struct blockcipher_info *info,
enum padding_type padding);
#ifndef LTC_NO_FILE
int pem_get_char_from_file(struct get_char *g);
#endif /* LTC_NO_FILE */
Expand Down
163 changes: 153 additions & 10 deletions src/misc/pem/pem.c
Expand Up @@ -47,21 +47,164 @@ const struct pem_header_id pem_std_headers[] = {
};
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") };
const struct str pem_dek_info_start = { SET_CSTR(, "DEK-Info: ") };
const struct dek_info_from_str pem_dek_infos[] =
const struct blockcipher_info pem_dek_infos[] =
{
{ SET_CSTR(.id, "AES-128-CBC,"), .info.alg = "aes", .info.keylen = 128 / 8, },
{ SET_CSTR(.id, "AES-192-CBC,"), .info.alg = "aes", .info.keylen = 192 / 8, },
{ SET_CSTR(.id, "AES-256-CBC,"), .info.alg = "aes", .info.keylen = 256 / 8, },
{ SET_CSTR(.id, "CAMELLIA-128-CBC,"), .info.alg = "camellia", .info.keylen = 128 / 8, },
{ SET_CSTR(.id, "CAMELLIA-192-CBC,"), .info.alg = "camellia", .info.keylen = 192 / 8, },
{ SET_CSTR(.id, "CAMELLIA-256-CBC,"), .info.alg = "camellia", .info.keylen = 256 / 8, },
{ SET_CSTR(.id, "DES-EDE3-CBC,"), .info.alg = "3des", .info.keylen = 192 / 8, },
{ SET_CSTR(.id, "DES-CBC,"), .info.alg = "des", .info.keylen = 64 / 8, },
{ .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-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-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 = "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-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-OFB,", .algo = "des", .keylen = 64 / 8, .mode = cm_ofb, },
{ .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 = "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,
const struct blockcipher_info *info,
enum padding_type padding)
{
int err, cipher;
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;

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:
#ifdef LTC_CFB_MODE
if ((err = cfb_start(cipher, iv, key, keylen, 0, &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;
default:
err = CRYPT_INVALID_ARG;
break;
}

error_out:
return err;
}

#endif /* LTC_PEM */
21 changes: 6 additions & 15 deletions src/misc/pem/pem_pkcs.c
Expand Up @@ -16,9 +16,12 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
{
unsigned char iv[MAXBLOCKSIZE], key[MAXBLOCKSIZE];
unsigned long ivlen, klen;
int err;
symmetric_CBC cbc_ctx;
int err, cipher;

cipher = find_cipher(hdr->info.algo);
if (cipher == -1) {
return CRYPT_INVALID_CIPHER;
}
if (hdr->info.keylen > sizeof(key)) {
return CRYPT_BUFFER_OVERFLOW;
}
Expand All @@ -35,20 +38,8 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
return err;
}

if ((err = cbc_start(find_cipher(hdr->info.alg), iv, key, klen, 0, &cbc_ctx)) != CRYPT_OK) {
goto error_out;
}
if ((err = cbc_decrypt(pem, pem, *l, &cbc_ctx)) != CRYPT_OK) {
goto error_out;
}
if ((err = cbc_done(&cbc_ctx)) != CRYPT_OK) {
goto error_out;
}
if ((err = padding_depad(pem, l, LTC_PAD_PKCS7 | cbc_ctx.blocklen)) != CRYPT_OK) {
goto error_out;
}
err = pem_decrypt(pem, l, key, klen, iv, ivlen, &hdr->info, LTC_PAD_PKCS7);

error_out:
zeromem(key, sizeof(key));
zeromem(iv, sizeof(iv));
return err;
Expand Down
16 changes: 9 additions & 7 deletions src/misc/pem/pem_read.c
Expand Up @@ -11,7 +11,7 @@

extern const struct str pem_proc_type_encrypted;
extern const struct str pem_dek_info_start;
extern const struct dek_info_from_str pem_dek_infos[];
extern const struct blockcipher_info pem_dek_infos[];
extern const unsigned long pem_dek_infos_num;

#ifndef LTC_NO_FILE
Expand Down Expand Up @@ -116,21 +116,23 @@ static int s_pem_decode_headers(struct pem_headers *hdr, struct get_char *g)
hdr->encrypted = 1;
break;
case 2:
hdr->info.alg = NULL;
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) {
if (slen >= pem_dek_infos[n].id.len + pem_dek_info_start.len && !XMEMCMP(alg_start, pem_dek_infos[n].id.p, pem_dek_infos[n].id.len)) {
hdr->info = pem_dek_infos[n].info;
tmplen = XSTRLEN(alg_start + pem_dek_infos[n].id.len);
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, alg_start + pem_dek_infos[n].id.len, tmplen);
XMEMCPY(hdr->info.iv, iv, tmplen);
break;
}
}
if (hdr->info.alg == NULL) {
if (hdr->info.algo == NULL) {
return CRYPT_INVALID_CIPHER;
}
break;
Expand Down

0 comments on commit 48bdd0f

Please sign in to comment.