Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PEM support #587

Merged
merged 51 commits into from
Aug 20, 2024
Merged
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
bee3ad2
add OpenSSH Private Key decryption demo
sjaeckel Sep 26, 2019
fec3d45
add rsa-support
sjaeckel Oct 1, 2019
2ff20d7
add ecdsa key support
sjaeckel Oct 2, 2019
e0046fb
add ssh private testkeys
sjaeckel Oct 2, 2019
350fbc6
refactor & clean-up
sjaeckel Oct 2, 2019
a301ea1
use updated API
sjaeckel Jan 21, 2020
cf71fff
re-factor openssh-privkey demo into library functions
sjaeckel Dec 26, 2021
69c93a8
rename file
sjaeckel Jan 6, 2022
35c306f
add `pk_get_oid_from_asn1()`
sjaeckel Jan 6, 2022
18baa14
add `der_flexi_sequence_cmp()`
sjaeckel Jan 6, 2022
b8cb13f
add `LTC_OID_MAX_STRLEN`
sjaeckel Jan 6, 2022
e9f1db1
add `pkcs8_get_children()`
sjaeckel Jan 6, 2022
3f6f885
re-factor PKCS#8 API a bit
sjaeckel Jan 11, 2022
f036a47
add support for regular PEM files
sjaeckel Jan 12, 2022
370457f
add PEM tests
sjaeckel Jan 12, 2022
464c9b1
add `dsa_import_pkcs8()`
sjaeckel Jan 14, 2022
22a952b
add DSA support to PEM decoder
sjaeckel Jan 14, 2022
d1e48df
Verify that the imported keys match
sjaeckel Jan 16, 2022
a76447f
add file-iterator to `test_process_dir()`
sjaeckel Jan 16, 2022
3e981af
also test FILE-based PEM API's
sjaeckel Jan 16, 2022
87a1758
split-up into multiple C files
sjaeckel Jan 22, 2022
26fbebb
add support for DH keys
sjaeckel Sep 13, 2022
5523ead
disable PEM support on MSVC
sjaeckel Mar 17, 2022
4e46f82
clean-up a bit
sjaeckel Aug 4, 2022
6515822
introduce `pka_key_free()`
sjaeckel Aug 4, 2022
39d4b08
Update docs
sjaeckel Aug 4, 2022
661f584
distinguish between Ed25519 and X25519
sjaeckel Aug 5, 2022
6c24c5c
Add support for `aes256-ctr` encrypted SSH keys
sjaeckel Oct 10, 2023
9333d5c
Use the public key contained within the ssh key
sjaeckel Oct 10, 2023
934abdd
Add "Enter passphrase" support to `openssh-privkey`
sjaeckel Oct 10, 2023
5d86589
Refactor some of the ECC import internals
sjaeckel Aug 4, 2022
f6497d2
Add support for more algos of encrypted PEM files
sjaeckel Oct 10, 2023
cda6211
pem: support for RC5-CBC, RC5-CFB, RC5-OFB
karel-m Oct 12, 2023
23eb8f9
Add support for importing PEM public keys
sjaeckel Oct 13, 2023
928c476
Per default support PEM line lengths up to 80 chars
sjaeckel Oct 13, 2023
ca7b4ee
Add more SSH key tests
sjaeckel Oct 13, 2023
7b5d85d
Calm old Valgrind ...
sjaeckel Oct 14, 2023
8d19047
Add support for more PEM file types + some fixes
sjaeckel Oct 16, 2023
fe3b3e6
Add generic PEM decode APIs
sjaeckel Oct 16, 2023
03515d5
Move password buffer into the library
sjaeckel Oct 17, 2023
9db30df
Make everything compile on MSVC
sjaeckel Oct 17, 2023
02cb0c4
Revert "Move password buffer into the library"
sjaeckel Oct 19, 2023
9579022
Add `free()` function to `password_ctx`
sjaeckel Oct 20, 2023
65e05bf
Update docs
sjaeckel Feb 26, 2024
c0be0aa
Add support for more types of encrypted PEM files
sjaeckel Feb 28, 2024
478f43f
Add support for reading `authorized_keys` files
sjaeckel Feb 29, 2024
ec8ffbb
Update makefiles
sjaeckel Feb 29, 2024
5dec1ee
Fix `make tvs`, add `make pr-check`
sjaeckel Mar 1, 2024
cf79fac
Add support for CFB1 and CFB8
sjaeckel Mar 3, 2024
2613264
We can now also decrypt PEM files encrypted in CFB1 and CFB8 mode
sjaeckel Mar 1, 2024
2594f3a
Update docs
sjaeckel Aug 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Revert "Move password buffer into the library"
This reverts commit d840323
sjaeckel committed Aug 20, 2024
commit 02cb0c49576b03f537922a437b4929b1412d58c2
23 changes: 13 additions & 10 deletions demos/openssh-privkey.c
Original file line number Diff line number Diff line change
@@ -29,11 +29,15 @@ static void die_(int err, int line)
#define die(i) do { die_(i, __LINE__); } while(0)
#define DIE(s, ...) do { print_err("%3d: " s "\n", __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); } while(0)

static int getpassword(const char *prompt, char *pass, unsigned long *len)
static char* getpassword(const char *prompt, size_t maxlen)
{
char *wr, *end, *pass = XCALLOC(1, maxlen + 1);
struct termios tio;
tcflag_t c_lflag;
unsigned long maxlen = *len, wr = 0;
if (pass == NULL)
return NULL;
wr = pass;
end = pass + maxlen;

tcgetattr(0, &tio);
c_lflag = tio.c_lflag;
@@ -42,25 +46,24 @@ static int getpassword(const char *prompt, char *pass, unsigned long *len)

printf("%s", prompt);
fflush(stdout);
while (1) {
while (pass < end) {
int c = getchar();
if (c == '\r' || c == '\n' || c == -1)
break;
if (wr < maxlen)
pass[wr] = c;
wr++;
*wr++ = c;
}
*len = wr;
tio.c_lflag = c_lflag;
tcsetattr(0, TCSAFLUSH, &tio);
printf("\n");
return wr <= maxlen;
return pass;
}

static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
(void)u;
return getpassword("Enter passphrase: ", p, l);
*p = getpassword("Enter passphrase: ", 256);
*l = strlen(*p);
return 0;
}

static void print(ltc_pka_key *k)
4 changes: 0 additions & 4 deletions src/headers/tomcrypt_custom.h
Original file line number Diff line number Diff line change
@@ -627,10 +627,6 @@
#define LTC_PBES
#endif

#if defined(LTC_PEM) || defined(LTC_PKCS_8) && !defined(LTC_MAX_PASSWORD_LEN)
#define LTC_MAX_PASSWORD_LEN 256
#endif

#if defined(LTC_CLEAN_STACK)
/* if you're sure that you want to use it, remove the line below */
#error LTC_CLEAN_STACK is considered as broken
11 changes: 8 additions & 3 deletions src/headers/tomcrypt_pk.h
Original file line number Diff line number Diff line change
@@ -5,12 +5,17 @@ typedef struct {
/**
Callback function that is called when a password is required.

@param str Pointer to where the password shall be stored.
@param len [in/out] The max length resp. resulting length of the password.
Please be aware that the library takes ownership of the pointer that is
returned to the library via `str`.
`str` shall be allocated via the same function as `XMALLOC` points to.
The data will be zeroed and `XFREE`'d as soon as it isn't required anymore.

@param str Pointer to pointer where the password will be stored.
@param len Pointer to the length of the password.
@param userdata `userdata` that was passed in the `password_ctx` struct.
@return CRYPT_OK on success
*/
int (*callback)(void *str, unsigned long *len, void *userdata);
int (*callback)(void **str, unsigned long *len, void *userdata);
/** Opaque `userdata` pointer passed when the callback is called */
void *userdata;
} password_ctx;
19 changes: 10 additions & 9 deletions src/headers/tomcrypt_private.h
Original file line number Diff line number Diff line change
@@ -83,18 +83,11 @@ typedef struct {
unsigned long blocklen;
} pbes_properties;

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

typedef struct
{
pbes_properties type;
struct password pwd;
void *pwd;
unsigned long pwdlen;
ltc_asn1_list *enc_data;
ltc_asn1_list *salt;
ltc_asn1_list *iv;
@@ -266,6 +259,14 @@ 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 blockcipher_info {
const char *name;
const char *algo;
3 changes: 0 additions & 3 deletions src/misc/crypt/crypt.c
Original file line number Diff line number Diff line change
@@ -470,9 +470,6 @@ const char *crypt_build_settings =
" PBES1 "
" PBES2 "
#endif
#if defined(LTC_MAX_PASSWORD_LEN)
" " NAME_VALUE(LTC_MAX_PASSWORD_LEN) " "
#endif
#if defined(LTC_PEM)
" PEM "
" " NAME_VALUE(LTC_PEM_DECODE_BUFSZ) " "
2 changes: 1 addition & 1 deletion src/misc/pbes/pbes.c
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ int pbes_decrypt(const pbes_arg *arg, unsigned char *dec_data, unsigned long *d

if (klen > sizeof(k)) return CRYPT_INVALID_ARG;

if ((err = arg->type.kdf(arg->pwd.pw, arg->pwd.l, arg->salt->data, arg->salt->size, arg->iterations, hid, k, &klen)) != CRYPT_OK) goto LBL_ERROR;
if ((err = arg->type.kdf(arg->pwd, arg->pwdlen, arg->salt->data, arg->salt->size, arg->iterations, hid, k, &klen)) != CRYPT_OK) goto LBL_ERROR;
if ((err = cbc_start(cid, iv, k, keylen, 0, &cbc)) != CRYPT_OK) goto LBL_ERROR;
if ((err = cbc_decrypt(arg->enc_data->data, dec_data, arg->enc_data->size, &cbc)) != CRYPT_OK) goto LBL_ERROR;
if ((err = cbc_done(&cbc)) != CRYPT_OK) goto LBL_ERROR;
11 changes: 7 additions & 4 deletions src/misc/pem/pem_pkcs.c
Original file line number Diff line number Diff line change
@@ -25,6 +25,9 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
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) {
@@ -199,7 +202,7 @@ static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_c
unsigned long w, l, n;
int err = CRYPT_ERROR;
struct pem_headers hdr = { 0 };
struct password pw = { 0 };
struct password pw;
enum ltc_pka_id pka;
XMEMSET(k, 0, sizeof(*k));
w = LTC_PEM_READ_BUFSIZE * 2;
@@ -238,8 +241,7 @@ static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_c
}

hdr.pw = &pw;
hdr.pw->l = LTC_MAX_PASSWORD_LEN;
if (pw_ctx->callback(hdr.pw->pw, &hdr.pw->l, pw_ctx->userdata)) {
if (pw_ctx->callback(&hdr.pw->pw, &hdr.pw->l, pw_ctx->userdata)) {
err = CRYPT_ERROR;
goto cleanup;
}
@@ -264,7 +266,8 @@ static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_c

cleanup:
if (hdr.pw) {
zeromem(hdr.pw->pw, sizeof(hdr.pw->pw));
zeromem(hdr.pw->pw, hdr.pw->l);
XFREE(hdr.pw->pw);
}
XFREE(pem);
return err;
7 changes: 3 additions & 4 deletions src/misc/pem/pem_ssh.c
Original file line number Diff line number Diff line change
@@ -570,8 +570,7 @@ static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_c
err = CRYPT_PW_CTX_MISSING;
goto cleanup;
}
opts.pw.l = LTC_MAX_PASSWORD_LEN;
if (pw_ctx->callback(opts.pw.pw, &opts.pw.l, pw_ctx->userdata)) {
if (pw_ctx->callback(&opts.pw.pw, &opts.pw.l, pw_ctx->userdata)) {
err = CRYPT_ERROR;
goto cleanup;
}
@@ -590,8 +589,8 @@ static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_c
}

cleanup:
if (opts.pw.l) {
zeromem(&opts.pw, sizeof(opts.pw));
if (opts.pw.pw) {
XFREE(opts.pw.pw);
}
if (privkey) {
zeromem(privkey, privkey_len);
12 changes: 7 additions & 5 deletions src/pk/asn1/pkcs8/pkcs8_decode_flexi.c
Original file line number Diff line number Diff line change
@@ -23,11 +23,13 @@ int pkcs8_decode_flexi(const unsigned char *in, unsigned long inlen,
unsigned char *dec_data = NULL;
ltc_asn1_list *l = NULL;
int err;
pbes_arg pbes = { 0 };
pbes_arg pbes;

LTC_ARGCHK(in != NULL);
LTC_ARGCHK(decoded_list != NULL);

XMEMSET(&pbes, 0, sizeof(pbes));

*decoded_list = NULL;
if ((err = der_decode_sequence_flexi(in, &len, &l)) == CRYPT_OK) {
/* the following "if" detects whether it is encrypted or not */
@@ -61,8 +63,7 @@ int pkcs8_decode_flexi(const unsigned char *in, unsigned long inlen,
goto LBL_DONE;
}

pbes.pwd.l = LTC_MAX_PASSWORD_LEN;
if (pw_ctx->callback(pbes.pwd.pw, &pbes.pwd.l, pw_ctx->userdata)) {
if (pw_ctx->callback(&pbes.pwd, &pbes.pwdlen, pw_ctx->userdata)) {
err = CRYPT_ERROR;
goto LBL_DONE;
}
@@ -94,8 +95,9 @@ int pkcs8_decode_flexi(const unsigned char *in, unsigned long inlen,

LBL_DONE:
if (l) der_free_sequence_flexi(l);
if (pbes.pwd.l) {
zeromem(&pbes.pwd, sizeof(pbes.pwd));
if (pbes.pwd) {
zeromem(pbes.pwd, pbes.pwdlen);
XFREE(pbes.pwd);
}
if (dec_data) {
zeromem(dec_data, dec_size);
8 changes: 3 additions & 5 deletions tests/ecc_test.c
Original file line number Diff line number Diff line change
@@ -660,14 +660,12 @@ static int s_ecc_new_api(void)
}


static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
int ret = *l < 6;
LTC_UNUSED_PARAM(u);
if (!ret)
XMEMCPY(p, "secret", 6);
*p = strdup("secret");
*l = 6;
return ret;
return 0;
}

static int s_ecc_import_export(void) {
8 changes: 3 additions & 5 deletions tests/ed25519_test.c
Original file line number Diff line number Diff line change
@@ -16,14 +16,12 @@ static void xor_shuffle(unsigned char *buf, unsigned long size, unsigned char ch
buf[i] ^= change;
}

static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
int ret = *l < 6;
LTC_UNUSED_PARAM(u);
if (!ret)
XMEMCPY(p, "123456", 6);
*p = strdup("123456");
*l = 6;
return ret;
return 0;
}

static int s_rfc_8410_10_test(void)
16 changes: 6 additions & 10 deletions tests/pem_test.c
Original file line number Diff line number Diff line change
@@ -6,14 +6,12 @@

#ifdef LTC_SSH

static int password_get_ssh(void *p, unsigned long *l, void *u)
static int password_get_ssh(void **p, unsigned long *l, void *u)
{
int ret = *l < 6;
LTC_UNUSED_PARAM(u);
if (!ret)
XMEMCPY(p, "abc123", 6);
*p = strdup("abc123");
*l = 6;
return ret;
return 0;
}
static int s_pem_decode_ssh(const void *in, unsigned long inlen, void *key)
{
@@ -30,14 +28,12 @@ static int s_pem_decode_ssh_f(FILE *f, void *key)

#endif

static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
int ret = *l < 6;
LTC_UNUSED_PARAM(u);
if (!ret)
XMEMCPY(p, "secret", 6);
*p = strdup("secret");
*l = 6;
return ret;
return 0;
}

#if defined(LTC_MDSA)
8 changes: 3 additions & 5 deletions tests/rsa_test.c
Original file line number Diff line number Diff line change
@@ -432,14 +432,12 @@ static int s_rsa_import_x509(const void *in, unsigned long inlen, void *key)
}

#if defined(LTC_MD2) && defined(LTC_MD5) && defined(LTC_RC2)
static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
int ret = *l < 6;
LTC_UNUSED_PARAM(u);
if (!ret)
XMEMCPY(p, "secret", 6);
*p = strdup("secret");
*l = 6;
return ret;
return 0;
}

static int s_rsa_import_pkcs8(const void *in, unsigned long inlen, void *key)
13 changes: 5 additions & 8 deletions tests/x25519_test.c
Original file line number Diff line number Diff line change
@@ -139,14 +139,11 @@ static int s_rfc_8410_10_test(void)
return CRYPT_OK;
}

static int password_get(void *p, unsigned long *l, void *u)
static int password_get(void **p, unsigned long *l, void *u)
{
unsigned long sl = strlen(u);
int ret = *l < sl;
if (!ret)
XMEMCPY(p, u, sl);
*l = sl;
return ret;
*p = strdup(u);
*l = strlen(*p);
return 0;
}

static int s_x25519_pkcs8_test(void)
@@ -165,7 +162,7 @@ static int s_x25519_pkcs8_test(void)
/* `openssl genpkey -algorithm x25519 -pass stdin` */
{
"MC4CAQAwBQYDK2VuBCIEIEAInaUdx+fQFfghpCzw/WdItRT3+FnPSkrU9TcIZTZW",
""
NULL
},
};
unsigned n;