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
Show file tree
Hide file tree
Changes from all commits
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .ci/Valgrind-Ubuntu_focal.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
<s_decode_header>
Memcheck:Cond
...
fun:s_decode_header
}
8 changes: 8 additions & 0 deletions .ci/meta_builds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ fi
function run_gcc() {
bash .ci/check_source.sh "CHECK_SOURCES" "$2" "$3" "$4" "$5"

make -j$(nproc) pem-info V=0

echo "verify docs..."
while read -r line; do
grep -q -e "$line" doc/crypt.tex || { echo "Failed to find \"$line\" in doc/crypt.tex"; exit 1; }
done < <(./pem-info | grep '^\\' | sed 's@\\@\\\\@g')
echo "docs OK"

make clean &>/dev/null

echo
Expand Down
9 changes: 8 additions & 1 deletion .ci/valgrind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ echo "Run tests with valgrind..."
for i in `seq 1 10` ; do sleep 300 && echo "Valgrind tests in Progress..."; done &
alive_pid=$!

valgrind --error-exitcode=666 --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all ./test >test_std.txt 2> >(tee -a test_err.txt >&2) || { kill $alive_pid; echo "Valgrind failed"; exit 1; }
readonly VALGRIND_OPTS="--error-exitcode=666 --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all"

readonly distro="$(lsb_release -si)_$(lsb_release -sc)"
readonly suppfile=".ci/Valgrind-${distro}.supp"
function get_suppfile() { [ -f "$suppfile" ] && echo "--suppressions=$suppfile" || echo ""; }
readonly VALGRIND_EXTRA_OPTS=$(get_suppfile)

valgrind $VALGRIND_OPTS $VALGRIND_EXTRA_OPTS ./test >test_std.txt 2> >(tee -a test_err.txt >&2) || { kill $alive_pid; echo "Valgrind failed"; exit 1; }

kill $alive_pid

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ multi
multi.exe
openssl-enc
openssl-enc.exe
openssh-privkey
openssh-privkey.exe
pem-info
pem-info.exe
sizes
sizes.exe
small
Expand Down
131 changes: 131 additions & 0 deletions demos/openssh-privkey.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */

/**
@file openssh-privkey.c
OpenSSH Private Key decryption demo, Steffen Jaeckel
*/

#include <tomcrypt.h>
#include <stdarg.h>
#include <termios.h>

#if defined(LTC_PEM_SSH)
static void print_err(const char *fmt, ...)
{
va_list args;

va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}

static void die_(int err, int line)
{
print_err("%3d: LTC sez %s\n", line, error_to_string(err));
exit(EXIT_FAILURE);
}

#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 char* getpassword(const char *prompt, size_t maxlen)
{
char *wr, *end, *pass = XCALLOC(1, maxlen + 1);
struct termios tio;
tcflag_t c_lflag;
if (pass == NULL)
return NULL;
wr = pass;
end = pass + maxlen;

tcgetattr(0, &tio);
c_lflag = tio.c_lflag;
tio.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &tio);

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

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

static void print(ltc_pka_key *k)
{
int err = CRYPT_OK;
unsigned char buf[256];
unsigned long lbuf = sizeof(buf);
char pubkey[256*4/3];
unsigned long lpubkey = sizeof(pubkey);
void *mpint = NULL;
switch (k->id) {
case LTC_PKA_ED25519:
ltc_mp.init(&mpint);
ltc_mp.unsigned_read(mpint, k->u.ed25519.pub, sizeof(k->u.ed25519.pub));
if ((err = ssh_encode_sequence_multi(buf, &lbuf,
LTC_SSHDATA_STRING, "ssh-ed25519", strlen("ssh-ed25519"),
LTC_SSHDATA_MPINT, mpint,
0, NULL)) != CRYPT_OK)
goto errout;
if ((err = base64_encode(buf, lbuf, pubkey, &lpubkey)) != CRYPT_OK)
goto errout;
printf("\rssh-ed25519 %s\n", pubkey);
break;
default:
print_err("Unsupported key type: %d\n", k->id);
break;
}
errout:
if (mpint != NULL)
ltc_mp.deinit(mpint);
if (err != CRYPT_OK)
die(err);
}

int main(int argc, char **argv)
{
int err;

FILE *f = NULL;
ltc_pka_key k;
password_ctx pw_ctx = { .callback = password_get };

if ((err = register_all_ciphers()) != CRYPT_OK) {
die(err);
}
if ((err = register_all_hashes()) != CRYPT_OK) {
die(err);
}
if ((err = crypt_mp_init("ltm")) != CRYPT_OK) {
die(err);
}

if (argc > 1) f = fopen(argv[1], "r");
else f = stdin;
if (f == NULL) DIE("fopen sez no");

if ((err = pem_decode_openssh_filehandle(f, &k, &pw_ctx))) {
die(err);
}
print(&k);
return EXIT_SUCCESS;
}
#else
int main(void) { return EXIT_FAILURE; }
#endif
101 changes: 101 additions & 0 deletions demos/pem-info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* print all PEM related infos */
#include "tomcrypt_private.h"

#if defined(LTC_PEM_SSH)
extern const struct blockcipher_info pem_dek_infos[];
extern const unsigned long pem_dek_infos_num;

extern const struct blockcipher_info ssh_ciphers[];
extern const unsigned long ssh_ciphers_num;

static const struct {
const char *is, *should;
} cipher_name_map[] = {
{ "", "none" },
{ "aes", "AES" },
{ "blowfish", "Blowfish" },
{ "c20p1305", "ChaCha20Poly1305" },
{ "camellia", "Camellia" },
{ "cast5", "CAST5" },
{ "chacha20", "ChaCha20" },
{ "3des", "3DES (EDE)" },
{ "des", "DES" },
{ "desx", "DES-X" },
{ "idea", "IDEA" },
{ "rc5", "RC5" },
{ "rc2", "RC2" },
{ "seed", "SEED" },
{ "serpent", "Serpent" },
{ "twofish", "Twofish" },
};

static const char *s_map_cipher(const char *name)
{
unsigned long n;
for (n = 0; n < sizeof(cipher_name_map)/sizeof(cipher_name_map[0]); ++n) {
if (strcmp(name, cipher_name_map[n].is) == 0)
return cipher_name_map[n].should;
}
fprintf(stderr, "Error: Can't map %s\n", name);
exit(1);
}

static const struct {
enum cipher_mode mode;
const char *name;
} cipher_mode_map[] = {
{ cm_none, "none", },
{ cm_cbc, "CBC", },
{ cm_cfb, "CFB", },
{ cm_ctr, "CTR", },
{ cm_ofb, "OFB", },
{ cm_stream, "STREAM", },
{ cm_gcm, "GCM", },
};

static const char *s_map_mode(enum cipher_mode mode)
{
size_t n;
mode &= cm_modes;
for (n = 0; n < sizeof(cipher_mode_map)/sizeof(cipher_mode_map[0]); ++n) {
if (cipher_mode_map[n].mode == mode)
return cipher_mode_map[n].name;
}
fprintf(stderr, "Error: Can't map cipher_mode %d\n", mode);
exit(1);
}

int main(void)
{
unsigned long n;
printf("PEM ciphers:\n\n");
for (n = 0; n < pem_dek_infos_num; ++n) {
char nbuf[32] = {0};
size_t nlen = strlen(pem_dek_infos[n].name);
memcpy(nbuf, pem_dek_infos[n].name, nlen);
nbuf[nlen-1] = '}';
printf("\\hline \\texttt{%-18s & %-15s & %-25ld & %-6s \\\\\n",
nbuf, s_map_cipher(pem_dek_infos[n].algo),
pem_dek_infos[n].keylen * 8,
s_map_mode(pem_dek_infos[n].mode));
}

printf("\nSSH ciphers:\n\n");
for (n = 0; n < ssh_ciphers_num; ++n) {
char nbuf[32] = {0};
size_t nlen = strlen(ssh_ciphers[n].name);
memcpy(nbuf, ssh_ciphers[n].name, nlen);
nbuf[nlen] = '}';
printf("\\hline \\texttt{%-30s & %-16s & %-24ld & %-6s \\\\\n",
nbuf, s_map_cipher(ssh_ciphers[n].algo),
ssh_ciphers[n].keylen * 8,
s_map_mode(ssh_ciphers[n].mode));
}

return 0;
}
#else
int main(void) { return EXIT_FAILURE; }
#endif
Loading