/* silcpgp_pkcs.c Author: Pekka Riikonen Copyright (C) 2007 - 2008 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "silccrypto.h" #include "rsa.h" #include "dsa.h" /**************************** OpenPGP PKCS API ******************************/ /* Get algorithm context */ SILC_PKCS_GET_ALGORITHM(silc_pkcs_pgp_get_algorithm) { SilcPGPPublicKey pubkey = public_key; return pubkey->pkcs; } /* Import PGP public key file */ SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_import_public_key_file) { SilcList list; SilcBool ret; unsigned char *data = NULL; SilcPGPPublicKey pubkey; SILC_LOG_DEBUG(("Parsing OpenPGP public key file")); if (!ret_public_key) return FALSE; switch (encoding) { case SILC_PKCS_FILE_BIN: break; case SILC_PKCS_FILE_BASE64: data = silc_pgp_dearmor(filedata, filedata_len, &filedata_len); if (!data) return FALSE; filedata = data; break; } /* Parse PGP packets */ if (!silc_pgp_packet_decode(filedata, filedata_len, NULL, &list)) { silc_free(data); return FALSE; } silc_free(data); /* Parse the public key */ ret = silc_pgp_public_key_decode(&list, &pubkey); if (ret) { if (ret_alg) *ret_alg = pubkey->pkcs; if (ret_public_key) *ret_public_key = pubkey; } silc_pgp_packet_free_list(&list); return ret; } /* Import OpenPGP public key packet (OpenPGP certificate). */ SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_pgp_import_public_key) { SilcPGPPublicKey pubkey; int ret; pubkey = silc_calloc(1, sizeof(*pubkey)); if (!pubkey) return 0; ret = silc_pgp_packet_public_key_decode(key, key_len, pubkey); if (ret) { if (ret_alg) *ret_alg = pubkey->pkcs; if (ret_public_key) *ret_public_key = pubkey; } else { silc_free(pubkey); } return ret; } /* Export PGP public key file */ SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_pgp_export_public_key_file) { return 0; } /* Export OpenPGP public key */ SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_pgp_export_public_key) { return 0; } /* Return public key length in bits */ SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_pgp_public_key_bitlen) { SilcPGPPublicKey pubkey = public_key; return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key); } /* Copy public key */ SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_pgp_public_key_copy) { SilcPGPPublicKey pubkey = public_key, new_pubkey, p; SilcPGPPacket packet; new_pubkey = silc_calloc(1, sizeof(*new_pubkey)); if (!new_pubkey) return NULL; if (pubkey->subkeys) { new_pubkey->subkeys = silc_dlist_init(); if (!new_pubkey->subkeys) { silc_free(new_pubkey); return NULL; } silc_dlist_start(pubkey->subkeys); while ((p = silc_dlist_get(pubkey->subkeys))) { p = silc_pkcs_pgp_public_key_copy(pkcs, p); if (p) silc_dlist_add(new_pubkey->subkeys, p); } } silc_list_init(new_pubkey->packets, struct SilcPGPPacketStruct, next); silc_list_start(pubkey->packets); while ((packet = silc_list_get(pubkey->packets))) { packet = silc_pgp_packet_copy(packet); if (packet) { silc_free(new_pubkey); return NULL; } silc_list_add(new_pubkey->packets, packet); } memcpy(new_pubkey->key_id, pubkey->key_id, sizeof(pubkey->key_id)); memcpy(new_pubkey->fingerprint, pubkey->fingerprint, sizeof(pubkey->fingerprint)); new_pubkey->created = pubkey->created; new_pubkey->valid = pubkey->valid; new_pubkey->version = pubkey->version; new_pubkey->algorithm = pubkey->algorithm; new_pubkey->public_key = pubkey->pkcs->public_key_copy(pubkey->pkcs, pubkey->public_key); if (!new_pubkey->public_key) { silc_free(new_pubkey); return NULL; } return new_pubkey; } /* Compares public keys */ SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_pgp_public_key_compare) { SilcPGPPublicKey k1 = key1, k2 = key2; if (k1->version != k2->version) return FALSE; if (k1->created != k2->created) return FALSE; if (k1->valid != k2->valid) return FALSE; if (k1->algorithm != k2->algorithm) return FALSE; if (memcmp(k1->key_id, k2->key_id, sizeof(k1->key_id))) return FALSE; if (memcmp(k1->fingerprint, k2->fingerprint, sizeof(k1->fingerprint))) return FALSE; return k1->pkcs->public_key_compare(k1->pkcs, k1->public_key, k2->public_key); } /* Free public key */ SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_pgp_public_key_free) { silc_pgp_public_key_free(public_key); } /* Import PGP private key file */ SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_import_private_key_file) { SilcList list; SilcBool ret; unsigned char *data = NULL; SilcPGPPrivateKey privkey; SILC_LOG_DEBUG(("Parsing OpenPGP private key file")); if (!ret_private_key) return FALSE; switch (encoding) { case SILC_PKCS_FILE_BIN: break; case SILC_PKCS_FILE_BASE64: data = silc_pgp_dearmor(filedata, filedata_len, &filedata_len); if (!data) return FALSE; filedata = data; break; } /* Parse PGP packets */ if (!silc_pgp_packet_decode(filedata, filedata_len, NULL, &list)) { silc_free(data); return FALSE; } silc_free(data); /* Parse the private key */ ret = silc_pgp_private_key_decode(&list, passphrase, passphrase_len, &privkey); if (ret) { if (ret_alg) *ret_alg = privkey->public_key->pkcs; if (ret_private_key) *ret_private_key = privkey; } silc_pgp_packet_free_list(&list); return ret; } /* Import OpenPGP private key */ SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_pgp_import_private_key) { SilcPGPPrivateKey privkey; int ret; privkey = silc_calloc(1, sizeof(*privkey)); if (!privkey) return 0; ret = silc_pgp_packet_private_key_decode(key, key_len, passphrase, passphrase_len, privkey); if (ret) { if (ret_alg) *ret_alg = privkey->public_key->pkcs; if (ret_private_key) *ret_private_key = privkey; } else { silc_free(privkey); } return ret; } /* Export PGP private key file */ SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_pgp_export_private_key_file) { return 0; } /* Export OpenPGP private key */ SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_pgp_export_private_key) { return 0; } /* Returns key length in bits */ SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_pgp_private_key_bitlen) { SilcPGPPrivateKey privkey = private_key; return silc_pkcs_pgp_public_key_bitlen(pkcs, privkey->public_key); } /* Free private key */ SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_pgp_private_key_free) { SilcPGPPrivateKey privkey = private_key; silc_pgp_private_key_free(privkey); } /* Encrypt */ SILC_PKCS_ENCRYPT(silc_pkcs_pgp_encrypt) { return 0; } /* Decrypt */ SILC_PKCS_DECRYPT(silc_pkcs_pgp_decrypt) { return 0; } /* Sign */ SILC_PKCS_SIGN(silc_pkcs_pgp_sign) { return 0; } /* Verify */ SILC_PKCS_VERIFY(silc_pkcs_pgp_verify) { return 0; } /************************** OpenPGP RSA PKCS API ****************************/ /* Import OpenPGP compliant RSA public key */ SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_rsa_import_public_key) { SilcBufferStruct alg_key; RsaPublicKey *pubkey; unsigned char *n, *e; SilcUInt16 n_len, e_len; if (!ret_public_key) return 0; /* Allocate RSA public key */ *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey)); if (!pubkey) return FALSE; /* Parse OpenPGP RSA public key */ silc_buffer_set(&alg_key, key, key_len); if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_UINT16(&n_len), SILC_STR_END) < 0) goto err; n_len = (n_len + 7) / 8; if (!n_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&n, n_len), SILC_STR_UINT16(&e_len), SILC_STR_END) < 0) goto err; e_len = (e_len + 7) / 8; if (!e_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&e, e_len), SILC_STR_END) < 0) goto err; /* Get MP integers */ silc_mp_init(&pubkey->n); silc_mp_init(&pubkey->e); silc_mp_bin2mp(n, n_len, &pubkey->n); silc_mp_bin2mp(e, e_len, &pubkey->e); /* Set key length */ pubkey->bits = silc_mp_sizeinbase(&pubkey->n, 2); return silc_buffer_headlen(&alg_key); err: silc_free(pubkey); return 0; } /* Export OpenPGP compliant RSA public key */ SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_rsa_export_public_key) { RsaPublicKey *pubkey = public_key; SilcBufferStruct alg_key; unsigned char *n = NULL, *e = NULL, *ret; SilcUInt16 n_len, e_len; n_len = silc_mp_sizeinbase(&pubkey->n, 2); e_len = silc_mp_sizeinbase(&pubkey->e, 2); /* Encode MP integers */ n = silc_mp_mp2bin(&pubkey->n, 0, NULL); if (!n) goto err; e = silc_mp_mp2bin(&pubkey->e, 0, NULL); if (!e) goto err; memset(&alg_key, 0, sizeof(alg_key)); if (silc_buffer_format(&alg_key, SILC_STR_UINT16(n_len), SILC_STR_DATA(n, n_len), SILC_STR_UINT16(e_len), SILC_STR_DATA(e, e_len), SILC_STR_END) < 0) goto err; silc_free(n); silc_free(e); ret = silc_buffer_steal(&alg_key, ret_len); return ret; err: silc_free(n); silc_free(e); return NULL; } /* Import OpenPGP compliant RSA private key */ SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_rsa_import_private_key) { SilcBufferStruct alg_key; RsaPrivateKey *privkey; unsigned char *d, *p, *q, *u; SilcUInt16 d_len, p_len, q_len, u_len; SilcMPInt pm1, qm1; if (!ret_private_key) return 0; /* Allocate RSA private key */ *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey)); if (!privkey) goto err; /* Parse OpenPGP RSA private key. In OpenPGP the u is p^-1 mod q, but our RSA implementation expects q^-1 mod p (PKCS#1 compliant), thus we reverse p and q to make it work. */ silc_buffer_set(&alg_key, key, key_len); if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_UINT16(&d_len), SILC_STR_END) < 0) goto err; d_len = (d_len + 7) / 8; if (!d_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&d, d_len), SILC_STR_UINT16(&q_len), SILC_STR_END) < 0) goto err; q_len = (q_len + 7) / 8; if (!q_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&q, q_len), SILC_STR_UINT16(&p_len), SILC_STR_END) < 0) goto err; p_len = (p_len + 7) / 8; if (!p_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&p, p_len), SILC_STR_UINT16(&u_len), SILC_STR_END) < 0) goto err; u_len = (u_len + 7) / 8; if (!u_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&u, u_len), SILC_STR_END) < 0) goto err; /* Get MP integers */ silc_mp_init(&privkey->d); silc_mp_init(&privkey->p); silc_mp_init(&privkey->q); silc_mp_init(&privkey->qP); silc_mp_bin2mp(d, d_len, &privkey->d); silc_mp_bin2mp(p, p_len, &privkey->p); silc_mp_bin2mp(q, q_len, &privkey->q); silc_mp_bin2mp(u, u_len, &privkey->qP); /* Fill in missing integers and pre-compute */ silc_mp_init(&pm1); silc_mp_init(&qm1); silc_mp_init(&privkey->n); silc_mp_init(&privkey->e); silc_mp_init(&privkey->dP); silc_mp_init(&privkey->dQ); silc_mp_mul(&privkey->n, &privkey->p, &privkey->q); silc_mp_sub_ui(&pm1, &privkey->p, 1); silc_mp_sub_ui(&qm1, &privkey->q, 1); silc_mp_mod(&privkey->dP, &privkey->d, &pm1); silc_mp_mod(&privkey->dQ, &privkey->d, &qm1); silc_mp_uninit(&pm1); silc_mp_uninit(&qm1); /* Set key length */ privkey->bits = silc_mp_sizeinbase(&privkey->n, 2); return silc_buffer_headlen(&alg_key); err: silc_free(privkey); return 0; } /* Export OpenPGP compliant RSA private key */ SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_rsa_export_private_key) { RsaPrivateKey *privkey = private_key; SilcBufferStruct alg_key; unsigned char *d = NULL, *p = NULL, *q = NULL, *u = NULL, *ret; SilcUInt16 d_len, p_len, q_len, u_len; /* In OpenPGP the u is p^-1 mod q, but our RSA implementation uses q^-1 mod p (PKCS#1 compliant), thus we reverse p and q to make the key correct. */ d_len = silc_mp_sizeinbase(&privkey->d, 2); p_len = silc_mp_sizeinbase(&privkey->q, 2); q_len = silc_mp_sizeinbase(&privkey->p, 2); u_len = silc_mp_sizeinbase(&privkey->qP, 2); /* Encode MP integers */ d = silc_mp_mp2bin(&privkey->d, 0, NULL); if (!d) goto err; p = silc_mp_mp2bin(&privkey->q, 0, NULL); if (!p) goto err; q = silc_mp_mp2bin(&privkey->p, 0, NULL); if (!q) goto err; u = silc_mp_mp2bin(&privkey->qP, 0, NULL); if (!u) goto err; memset(&alg_key, 0, sizeof(alg_key)); if (silc_buffer_format(&alg_key, SILC_STR_UINT16(d_len), SILC_STR_DATA(d, d_len), SILC_STR_UINT16(p_len), SILC_STR_DATA(p, p_len), SILC_STR_UINT16(q_len), SILC_STR_DATA(q, q_len), SILC_STR_UINT16(u_len), SILC_STR_DATA(u, u_len), SILC_STR_END) < 0) goto err; silc_free(d); silc_free(p); silc_free(q); silc_free(u); ret = silc_buffer_steal(&alg_key, ret_len); return ret; err: silc_free(d); silc_free(p); silc_free(q); silc_free(u); return NULL; } /************************** OpenPGP DSA PKCS API ****************************/ /* Import OpenPGP compliant DSA public key */ SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_pgp_dsa_import_public_key) { SilcBufferStruct alg_key; DsaPublicKey *pubkey; unsigned char *p, *q, *g, *y; SilcUInt16 p_len, q_len, g_len, y_len; if (!ret_public_key) return 0; /* Allocate DSA public key */ *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey)); if (!pubkey) return FALSE; /* Parse OpenPGP DSA public key */ silc_buffer_set(&alg_key, key, key_len); if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_UINT16(&p_len), SILC_STR_END) < 0) goto err; p_len = (p_len + 7) / 8; if (!p_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&p, p_len), SILC_STR_UINT16(&q_len), SILC_STR_END) < 0) goto err; q_len = (q_len + 7) / 8; if (!q_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&q, q_len), SILC_STR_UINT16(&g_len), SILC_STR_END) < 0) goto err; g_len = (g_len + 7) / 8; if (!g_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&g, g_len), SILC_STR_UINT16(&y_len), SILC_STR_END) < 0) goto err; y_len = (y_len + 7) / 8; if (!y_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&y, y_len), SILC_STR_END) < 0) goto err; /* Get MP integers */ silc_mp_init(&pubkey->p); silc_mp_init(&pubkey->q); silc_mp_init(&pubkey->g); silc_mp_init(&pubkey->y); silc_mp_bin2mp(p, p_len, &pubkey->p); silc_mp_bin2mp(q, q_len, &pubkey->q); silc_mp_bin2mp(g, g_len, &pubkey->g); silc_mp_bin2mp(y, y_len, &pubkey->y); /* Set key length */ pubkey->bits = silc_mp_sizeinbase(&pubkey->p, 2); return silc_buffer_headlen(&alg_key); err: silc_free(pubkey); return 0; } /* Export OpenPGP compliant DSA public key */ SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_pgp_dsa_export_public_key) { return 0; } /* Import OpenPGP compliant DSA private key */ SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_pgp_dsa_import_private_key) { SilcBufferStruct alg_key; DsaPrivateKey *privkey; unsigned char *x; SilcUInt16 x_len; if (!ret_private_key) return 0; /* Allocate DSA private key */ *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey)); if (!privkey) goto err; /* Parse OpenPGP DSA private key. */ silc_buffer_set(&alg_key, key, key_len); if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_UINT16(&x_len), SILC_STR_END) < 0) goto err; x_len = (x_len + 7) / 8; if (!x_len) goto err; if (silc_buffer_unformat(&alg_key, SILC_STR_ADVANCE, SILC_STR_DATA(&x, x_len), SILC_STR_END) < 0) goto err; /* Get MP integers */ silc_mp_init(&privkey->x); silc_mp_bin2mp(x, x_len, &privkey->x); return silc_buffer_headlen(&alg_key); err: silc_free(privkey); return 0; } /* Export OpenPGP compliant DSA private key */ SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_pgp_dsa_export_private_key) { return 0; }