diff --git a/configure.in b/configure.in index dd16633a7..8df0c2a0b 100644 --- a/configure.in +++ b/configure.in @@ -112,6 +112,7 @@ ARG_ENABL_SET([ldap], [enable LDAP fetching plugin to fetch files via ARG_DISBL_SET([aes], [disable AES software implementation plugin.]) ARG_DISBL_SET([des], [disable DES/3DES software implementation plugin.]) ARG_ENABL_SET([blowfish], [enable Blowfish software implementation plugin.]) +ARG_DISBL_SET([rc2], [disable RC2 software implementation plugin.]) ARG_ENABL_SET([md4], [enable MD4 software implementation plugin.]) ARG_DISBL_SET([md5], [disable MD5 software implementation plugin.]) ARG_DISBL_SET([sha1], [disable SHA1 software implementation plugin.]) @@ -128,6 +129,7 @@ ARG_DISBL_SET([pubkey], [disable RAW public key support plugin.]) ARG_DISBL_SET([pkcs1], [disable PKCS1 key decoding plugin.]) ARG_DISBL_SET([pkcs7], [disable PKCS7 container support plugin.]) ARG_DISBL_SET([pkcs8], [disable PKCS8 private key decoding plugin.]) +ARG_DISBL_SET([pkcs12], [disable PKCS12 container support plugin.]) ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.]) ARG_DISBL_SET([dnskey], [disable DNS RR key decoding plugin.]) ARG_DISBL_SET([sshkey], [disable SSH key decoding plugin.]) @@ -952,6 +954,7 @@ ADD_PLUGIN([pkcs11], [s charon pki nm cmd]) ADD_PLUGIN([aes], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([des], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([blowfish], [s charon openac scepclient pki scripts nm cmd]) +ADD_PLUGIN([rc2], [s charon openac scepclient pki scripts nm cmd]) ADD_PLUGIN([sha1], [s charon openac scepclient pki scripts medsrv attest nm cmd]) ADD_PLUGIN([sha2], [s charon openac scepclient pki scripts medsrv attest nm cmd]) ADD_PLUGIN([md4], [s charon openac manager scepclient pki nm cmd]) @@ -964,8 +967,9 @@ ADD_PLUGIN([revocation], [s charon nm cmd]) ADD_PLUGIN([constraints], [s charon nm cmd]) ADD_PLUGIN([pubkey], [s charon cmd]) ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) -ADD_PLUGIN([pkcs7], [s scepclient pki]) +ADD_PLUGIN([pkcs7], [s charon scepclient pki scripts nm cmd]) ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) +ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd]) ADD_PLUGIN([pgp], [s charon]) ADD_PLUGIN([dnskey], [s charon]) ADD_PLUGIN([sshkey], [s charon nm cmd]) @@ -1081,6 +1085,7 @@ AM_CONDITIONAL(USE_LDAP, test x$ldap = xtrue) AM_CONDITIONAL(USE_AES, test x$aes = xtrue) AM_CONDITIONAL(USE_DES, test x$des = xtrue) AM_CONDITIONAL(USE_BLOWFISH, test x$blowfish = xtrue) +AM_CONDITIONAL(USE_RC2, test x$rc2 = xtrue) AM_CONDITIONAL(USE_MD4, test x$md4 = xtrue) AM_CONDITIONAL(USE_MD5, test x$md5 = xtrue) AM_CONDITIONAL(USE_SHA1, test x$sha1 = xtrue) @@ -1097,6 +1102,7 @@ AM_CONDITIONAL(USE_PUBKEY, test x$pubkey = xtrue) AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue) AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue) AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue) +AM_CONDITIONAL(USE_PKCS12, test x$pkcs12 = xtrue) AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue) AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue) AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue) @@ -1274,6 +1280,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/cmac/Makefile src/libstrongswan/plugins/des/Makefile src/libstrongswan/plugins/blowfish/Makefile + src/libstrongswan/plugins/rc2/Makefile src/libstrongswan/plugins/md4/Makefile src/libstrongswan/plugins/md5/Makefile src/libstrongswan/plugins/sha1/Makefile @@ -1292,6 +1299,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/pkcs1/Makefile src/libstrongswan/plugins/pkcs7/Makefile src/libstrongswan/plugins/pkcs8/Makefile + src/libstrongswan/plugins/pkcs12/Makefile src/libstrongswan/plugins/pgp/Makefile src/libstrongswan/plugins/dnskey/Makefile src/libstrongswan/plugins/sshkey/Makefile diff --git a/man/ipsec.secrets.5.in b/man/ipsec.secrets.5.in index 319d4856b..ee20c9670 100644 --- a/man/ipsec.secrets.5.in +++ b/man/ipsec.secrets.5.in @@ -91,6 +91,9 @@ defines an RSA private key .B ECDSA defines an ECDSA private key .TP +.B P12 +defines a PKCS#12 container +.TP .B EAP defines EAP credentials .TP @@ -133,16 +136,26 @@ Similarly, a character sequence beginning with .B 0s is interpreted as Base64 encoded binary data. .TP -.B [ ] : RSA [ | %prompt ] +.B : RSA [ | %prompt ] .TQ -.B [ ] : ECDSA [ | %prompt ] +.B : ECDSA [ | %prompt ] For the private key file both absolute paths or paths relative to \fI/etc/ipsec.d/private\fP are accepted. If the private key file is encrypted, the \fIpassphrase\fP must be defined. Instead of a passphrase .B %prompt -can be used which then causes the daemons to ask the user for the password +can be used which then causes the daemon to ask the user for the password whenever it is required to decrypt the key. .TP +.B : P12 [ | %prompt ] +For the PKCS#12 file both absolute paths or paths relative to +\fI/etc/ipsec.d/private\fP are accepted. If the container is +encrypted, the \fIpassphrase\fP must be defined. Instead of a passphrase +.B %prompt +can be used which then causes the daemon to ask the user for the password +whenever it is required to decrypt the container. Private keys, client and CA +certificates are extracted from the container. To use such a client certificate +in a connection set leftid to one of the subjects of the certificate. +.TP .B : EAP The format of \fIsecret\fP is the same as that of \fBPSK\fP secrets. .br @@ -165,7 +178,7 @@ key. The slot number defines the slot on the token, the module name refers to the module name defined in strongswan.conf(5). Instead of specifying the pin code statically, .B %prompt -can be specified, which causes the daemons to ask the user for the pin code. +can be specified, which causes the daemon to ask the user for the pin code. .LP .SH FILES diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c index 9c25df907..e48f54887 100644 --- a/src/charon-cmd/cmd/cmd_connection.c +++ b/src/charon-cmd/cmd/cmd_connection.c @@ -391,6 +391,7 @@ METHOD(cmd_connection_t, handle, bool, break; case CMD_OPT_RSA: case CMD_OPT_AGENT: + case CMD_OPT_PKCS12: this->key_seen = TRUE; break; case CMD_OPT_LOCAL_TS: diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c index 98337db55..526ff7c9c 100644 --- a/src/charon-cmd/cmd/cmd_creds.c +++ b/src/charon-cmd/cmd/cmd_creds.c @@ -22,6 +22,7 @@ #include #include +#include #include typedef struct private_cmd_creds_t private_cmd_creds_t; @@ -70,6 +71,7 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, identification_t *me, identification_t *other, id_match_t *match_me, id_match_t *match_other) { + shared_key_t *shared; char *label, *pwd; if (this->prompted) @@ -84,6 +86,9 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, case SHARED_IKE: label = "Preshared Key: "; break; + case SHARED_PRIVATE_KEY_PASS: + label = "Password: "; + break; default: return NULL; } @@ -93,8 +98,18 @@ static shared_key_t* callback_shared(private_cmd_creds_t *this, return NULL; } this->prompted = TRUE; - *match_me = *match_other = ID_MATCH_PERFECT; - return shared_key_create(type, chunk_clone(chunk_from_str(pwd))); + if (match_me) + { + *match_me = ID_MATCH_PERFECT; + } + if (match_other) + { + *match_other = ID_MATCH_PERFECT; + } + shared = shared_key_create(type, chunk_clone(chunk_from_str(pwd))); + /* cache password in case it is required more than once */ + this->creds->add_shared(this->creds, shared, NULL); + return shared->get_ref(shared); } /** @@ -172,6 +187,40 @@ static void load_agent(private_cmd_creds_t *this) this->creds->add_key(this->creds, privkey); } +/** + * Load a PKCS#12 file from path + */ +static void load_pkcs12(private_cmd_creds_t *this, char *path) +{ + enumerator_t *enumerator; + certificate_t *cert; + private_key_t *key; + container_t *container; + pkcs12_t *pkcs12; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12, + BUILD_FROM_FILE, path, BUILD_END); + if (!container) + { + DBG1(DBG_CFG, "loading PKCS#12 file '%s' failed", path); + exit(1); + } + pkcs12 = (pkcs12_t*)container; + enumerator = pkcs12->create_cert_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &cert)) + { + this->creds->add_cert(this->creds, TRUE, cert->get_ref(cert)); + } + enumerator->destroy(enumerator); + enumerator = pkcs12->create_key_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &key)) + { + this->creds->add_key(this->creds, key->get_ref(key)); + } + enumerator->destroy(enumerator); + container->destroy(container); +} + METHOD(cmd_creds_t, handle, bool, private_cmd_creds_t *this, cmd_option_type_t opt, char *arg) { @@ -183,6 +232,9 @@ METHOD(cmd_creds_t, handle, bool, case CMD_OPT_RSA: load_key(this, KEY_RSA, arg); break; + case CMD_OPT_PKCS12: + load_pkcs12(this, arg); + break; case CMD_OPT_IDENTITY: this->identity = arg; break; diff --git a/src/charon-cmd/cmd/cmd_options.c b/src/charon-cmd/cmd/cmd_options.c index 06d0996cc..e7dbff7e0 100644 --- a/src/charon-cmd/cmd/cmd_options.c +++ b/src/charon-cmd/cmd/cmd_options.c @@ -38,6 +38,10 @@ cmd_option_t cmd_options[CMD_OPT_COUNT] = { "trusted certificate, for authentication or trust chain validation", {}}, { CMD_OPT_RSA, "rsa", required_argument, "path", "RSA private key to use for authentication", {}}, + { CMD_OPT_PKCS12, "p12", required_argument, "path", + "PKCS#12 file with private key and certificates to use for ", { + "authentication and trust chain validation" + }}, { CMD_OPT_AGENT, "agent", optional_argument, "socket", "use SSH agent for authentication. If socket is not specified", { "it is read from the SSH_AUTH_SOCK environment variable", diff --git a/src/charon-cmd/cmd/cmd_options.h b/src/charon-cmd/cmd/cmd_options.h index a14896f83..7a6080f3a 100644 --- a/src/charon-cmd/cmd/cmd_options.h +++ b/src/charon-cmd/cmd/cmd_options.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2013 Martin Willi * Copyright (C) 2013 revosec AG * @@ -35,6 +38,7 @@ enum cmd_option_type_t { CMD_OPT_REMOTE_IDENTITY, CMD_OPT_CERT, CMD_OPT_RSA, + CMD_OPT_PKCS12, CMD_OPT_AGENT, CMD_OPT_LOCAL_TS, CMD_OPT_REMOTE_TS, diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index f24082ee3..6b37ac787 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,7 @@ struct private_stroke_cred_t { /** * ignore missing CA basic constraint (i.e. treat all certificates in - * ipsec.conf ca sections and ipsec.d/cacert as CA certificates) + * ipsec.conf ca sections and ipsec.d/cacerts as CA certificates) */ bool force_ca_cert; @@ -225,7 +226,7 @@ METHOD(stroke_cred_t, load_ca, certificate_t*, cert->destroy(cert); return NULL; } - DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s", + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", cert->get_subject(cert), filename); return this->creds->add_cert_ref(this->creds, TRUE, cert); } @@ -599,8 +600,12 @@ static err_t extract_secret(chunk_t *secret, chunk_t *line) * Data for passphrase callback */ typedef struct { + /** cached passphrases */ + mem_cred_t *cache; /** socket we use for prompting */ FILE *prompt; + /** type of secret to unlock */ + int type; /** private key file */ char *path; /** number of tries */ @@ -608,13 +613,15 @@ typedef struct { } passphrase_cb_data_t; /** - * Callback function to receive Passphrases + * Callback function to receive passphrases */ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, - shared_key_type_t type, - identification_t *me, identification_t *other, - id_match_t *match_me, id_match_t *match_other) + shared_key_type_t type, identification_t *me, + identification_t *other, id_match_t *match_me, + id_match_t *match_other) { + static const int max_tries = 3; + shared_key_t *shared; chunk_t secret; char buf[256]; @@ -623,17 +630,23 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, return NULL; } + data->try++; + if (data->try > max_tries + 1) + { /* another builder might call this after we gave up, fail silently */ + return NULL; + } + if (data->try > max_tries) + { + fprintf(data->prompt, "Passphrase invalid, giving up.\n"); + return NULL; + } if (data->try > 1) { - if (data->try > 5) - { - fprintf(data->prompt, "PIN invalid, giving up.\n"); - return NULL; - } - fprintf(data->prompt, "PIN invalid!\n"); + fprintf(data->prompt, "Passphrase invalid!\n"); } - data->try++; - fprintf(data->prompt, "Private key '%s' is encrypted.\n", data->path); + fprintf(data->prompt, "%s '%s' is encrypted.\n", + data->type == CRED_PRIVATE_KEY ? "Private key" : "PKCS#12 file", + data->path); fprintf(data->prompt, "Passphrase:\n"); if (fgets(buf, sizeof(buf), data->prompt)) { @@ -649,7 +662,10 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data, { *match_other = ID_MATCH_NONE; } - return shared_key_create(SHARED_PRIVATE_KEY_PASS, chunk_clone(secret)); + shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, + chunk_clone(secret)); + data->cache->add_shared(data->cache, shared->get_ref(shared), NULL); + return shared; } } return NULL; @@ -689,12 +705,12 @@ static shared_key_t* pin_cb(pin_cb_data_t *data, shared_key_type_t type, return NULL; } + data->try++; if (data->try > 1) { fprintf(data->prompt, "PIN invalid, aborting.\n"); return NULL; } - data->try++; fprintf(data->prompt, "Login to '%s' required\n", data->card); fprintf(data->prompt, "PIN:\n"); if (fgets(buf, sizeof(buf), data->prompt)) @@ -783,7 +799,7 @@ static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr, pin_data.prompt = prompt; pin_data.card = smartcard; pin_data.keyid = chunk; - pin_data.try = 1; + pin_data.try = 0; cb = callback_cred_create_shared((void*)pin_cb, &pin_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); } @@ -821,15 +837,14 @@ static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr, } /** - * Load a private key + * Load a private key or PKCS#12 container from a file */ -static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, - FILE *prompt, key_type_t key_type) +static bool load_from_file(chunk_t line, int line_nr, FILE *prompt, + char *path, int type, int subtype, + void **result) { - char path[PATH_MAX]; chunk_t filename; chunk_t secret = chunk_empty; - private_key_t *key; err_t ugh = extract_value(&filename, &line); @@ -846,12 +861,12 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, if (*filename.ptr == '/') { /* absolute path name */ - snprintf(path, sizeof(path), "%.*s", (int)filename.len, filename.ptr); + snprintf(path, PATH_MAX, "%.*s", (int)filename.len, filename.ptr); } else { /* relative path name */ - snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR, + snprintf(path, PATH_MAX, "%s/%.*s", PRIVATE_KEY_DIR, (int)filename.len, filename.ptr); } @@ -867,30 +882,35 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, } if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7)) { - callback_cred_t *cb = NULL; + callback_cred_t *cb; passphrase_cb_data_t pp_data = { .prompt = prompt, + .type = type, .path = path, - .try = 1, + .try = 0, }; free(secret.ptr); if (!prompt) { + *result = NULL; return TRUE; } + /* add cache first so if valid passphrases are needed multiple times + * the callback is not called anymore */ + pp_data.cache = mem_cred_create(); + lib->credmgr->add_local_set(lib->credmgr, &pp_data.cache->set, FALSE); /* use callback credential set to prompt for the passphrase */ - pp_data.prompt = prompt; - pp_data.path = path; - pp_data.try = 1; cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data); lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE); - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, - BUILD_FROM_FILE, path, BUILD_END); + *result = lib->creds->create(lib->creds, type, subtype, + BUILD_FROM_FILE, path, BUILD_END); lib->credmgr->remove_local_set(lib->credmgr, &cb->set); cb->destroy(cb); + lib->credmgr->remove_local_set(lib->credmgr, &pp_data.cache->set); + pp_data.cache->destroy(pp_data.cache); } else { @@ -901,14 +921,44 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret); mem = mem_cred_create(); mem->add_shared(mem, shared, NULL); + if (eat_whitespace(&line)) + { /* if there is a second passphrase add that too, could be needed for + * PKCS#12 files using different passwords for MAC and encryption */ + ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh); + mem->destroy(mem); + return FALSE; + } + shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret); + mem->add_shared(mem, shared, NULL); + } lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE); - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, - BUILD_FROM_FILE, path, BUILD_END); + *result = lib->creds->create(lib->creds, type, subtype, + BUILD_FROM_FILE, path, BUILD_END); lib->credmgr->remove_local_set(lib->credmgr, &mem->set); mem->destroy(mem); } + return TRUE; +} + +/** + * Load a private key + */ +static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, + FILE *prompt, key_type_t key_type) +{ + char path[PATH_MAX]; + private_key_t *key; + + if (!load_from_file(line, line_nr, prompt, path, CRED_PRIVATE_KEY, + key_type, (void**)&key)) + { + return FALSE; + } if (key) { DBG1(DBG_CFG, " loaded %N private key from '%s'", @@ -922,6 +972,58 @@ static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr, return TRUE; } +/** + * Load a PKCS#12 container + */ +static bool load_pkcs12(mem_cred_t *secrets, chunk_t line, int line_nr, + FILE *prompt) +{ + enumerator_t *enumerator; + char path[PATH_MAX]; + certificate_t *cert; + private_key_t *key; + pkcs12_t *pkcs12; + + if (!load_from_file(line, line_nr, prompt, path, CRED_CONTAINER, + CONTAINER_PKCS12, (void**)&pkcs12)) + { + return FALSE; + } + if (!pkcs12) + { + DBG1(DBG_CFG, " loading credentials from '%s' failed", path); + return TRUE; + } + enumerator = pkcs12->create_cert_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + + if (x509->get_flags(x509) & X509_CA) + { + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", + cert->get_subject(cert), path); + } + else + { + DBG1(DBG_CFG, " loaded certificate \"%Y\" from '%s'", + cert->get_subject(cert), path); + } + secrets->add_cert(secrets, TRUE, cert->get_ref(cert)); + } + enumerator->destroy(enumerator); + enumerator = pkcs12->create_key_enumerator(pkcs12); + while (enumerator->enumerate(enumerator, &key)) + { + DBG1(DBG_CFG, " loaded %N private key from '%s'", + key_type_names, key->get_type(key), path); + secrets->add_key(secrets, key->get_ref(key)); + } + enumerator->destroy(enumerator); + pkcs12->container.destroy(&pkcs12->container); + return TRUE; +} + /** * Load a shared key */ @@ -1140,6 +1242,13 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets, break; } } + else if (match("P12", &token)) + { + if (!load_pkcs12(secrets, line, line_nr, prompt)) + { + break; + } + } else if (match("PIN", &token)) { if (!load_pin(secrets, line, line_nr, prompt)) @@ -1160,7 +1269,7 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets, else { DBG1(DBG_CFG, "line %d: token must be either " - "RSA, ECDSA, PSK, EAP, XAUTH or PIN", line_nr); + "RSA, ECDSA, P12, PIN, PSK, EAP, XAUTH or NTLM", line_nr); break; } } diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index a46b0d9a1..75b501fdd 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -8,7 +8,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ -crypto/prfs/prf.c crypto/prfs/mac_prf.c \ +crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \ crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \ @@ -17,7 +17,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index b5a4b9bab..2465fb09d 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -6,7 +6,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ -crypto/prfs/prf.c crypto/prfs/mac_prf.c \ +crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \ crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \ @@ -15,7 +15,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ @@ -45,7 +45,7 @@ crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \ crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \ crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \ crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \ -crypto/aead.h crypto/transform.h \ +crypto/aead.h crypto/transform.h crypto/pkcs5.h \ credentials/credential_factory.h credentials/builder.h \ credentials/cred_encoding.h credentials/keys/private_key.h \ credentials/keys/public_key.h credentials/keys/shared_key.h \ @@ -55,6 +55,7 @@ credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \ credentials/certificates/ocsp_response.h \ credentials/certificates/pgp_certificate.h \ credentials/containers/container.h credentials/containers/pkcs7.h \ +credentials/containers/pkcs12.h \ credentials/ietf_attributes/ietf_attributes.h \ credentials/credential_manager.h credentials/sets/auth_cfg_wrapper.h \ credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \ @@ -175,6 +176,13 @@ if MONOLITHIC endif endif +if USE_RC2 + SUBDIRS += plugins/rc2 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/rc2/libstrongswan-rc2.la +endif +endif + if USE_MD4 SUBDIRS += plugins/md4 if MONOLITHIC @@ -301,6 +309,13 @@ if MONOLITHIC endif endif +if USE_PKCS12 + SUBDIRS += plugins/pkcs12 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/pkcs12/libstrongswan-pkcs12.la +endif +endif + if USE_PGP SUBDIRS += plugins/pgp if MONOLITHIC diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c index f438cb20e..68f37f471 100644 --- a/src/libstrongswan/asn1/asn1.c +++ b/src/libstrongswan/asn1/asn1.c @@ -549,6 +549,22 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c return TRUE; } +/* + * Described in header + */ +u_int64_t asn1_parse_integer_uint64(chunk_t blob) +{ + u_int64_t val = 0; + int i; + + for (i = 0; i < blob.len; i++) + { /* if it is longer than 8 bytes, we just use the 8 LSBs */ + val <<= 8; + val |= (u_int64_t)blob.ptr[i]; + } + return val; +} + /** * ASN.1 definition of an algorithmIdentifier */ diff --git a/src/libstrongswan/asn1/asn1.h b/src/libstrongswan/asn1/asn1.h index 15ffff62e..a1d625380 100644 --- a/src/libstrongswan/asn1/asn1.h +++ b/src/libstrongswan/asn1/asn1.h @@ -170,6 +170,15 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *params); bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level0, const char* name); +/** + * Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer + * than 8 bytes only the 8 LSBs are returned. + * + * @param blob body of an ASN.1 coded integer object + * @return converted integer + */ +u_int64_t asn1_parse_integer_uint64(chunk_t blob); + /** * Print the value of an ASN.1 simple object * diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt index 49ef1cdf2..6030aa111 100644 --- a/src/libstrongswan/asn1/oid.txt +++ b/src/libstrongswan/asn1/oid.txt @@ -121,6 +121,25 @@ 0x08 "unstructuredAddress" OID_UNSTRUCTURED_ADDRESS 0x0E "extensionRequest" OID_EXTENSION_REQUEST 0x0F "S/MIME Capabilities" + 0x16 "certTypes" + 0x01 "X.509" OID_X509_CERTIFICATE + 0x02 "SDSI" + 0x0c "PKCS-12" + 0x01 "pbeIds" + 0x01 "pbeWithSHAAnd128BitRC4" OID_PBE_SHA1_RC4_128 + 0x02 "pbeWithSHAAnd40BitRC4" OID_PBE_SHA1_RC4_40 + 0x03 "pbeWithSHAAnd3-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_CBC + 0x04 "pbeWithSHAAnd2-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_2KEY_CBC + 0x05 "pbeWithSHAAnd128BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_128 + 0x06 "pbeWithSHAAnd40BitRC2-CBC" OID_PBE_SHA1_RC2_CBC_40 + 0x0a "PKCS-12v1" + 0x01 "bagIds" + 0x01 "keyBag" OID_P12_KEY_BAG + 0x02 "pkcs8ShroudedKeyBag" OID_P12_PKCS8_KEY_BAG + 0x03 "certBag" OID_P12_CERT_BAG + 0x04 "crlBag" OID_P12_CRL_BAG + 0x05 "secretBag" + 0x06 "safeContentsBag" 0x02 "digestAlgorithm" 0x02 "md2" OID_MD2 0x05 "md5" OID_MD5 @@ -240,7 +259,7 @@ 0x05 "caRepository" 0x08 "ipsec" 0x02 "certificate" - 0x02 "iKEIntermediate" OID_IKE_INTERMEDIATE + 0x02 "iKEIntermediate" OID_IKE_INTERMEDIATE 0x0E "oiw" 0x03 "secsig" 0x02 "algorithms" diff --git a/src/libstrongswan/credentials/certificates/certificate.c b/src/libstrongswan/credentials/certificates/certificate.c index bc4209ca7..b281c1669 100644 --- a/src/libstrongswan/credentials/certificates/certificate.c +++ b/src/libstrongswan/credentials/certificates/certificate.c @@ -18,7 +18,7 @@ #include #include -ENUM(certificate_type_names, CERT_ANY, CERT_PLUTO_CRL, +ENUM(certificate_type_names, CERT_ANY, CERT_GPG, "ANY", "X509", "X509_CRL", @@ -28,9 +28,6 @@ ENUM(certificate_type_names, CERT_ANY, CERT_PLUTO_CRL, "TRUSTED_PUBKEY", "PKCS10_REQUEST", "PGP", - "PLUTO_CERT", - "PLUTO_AC", - "PLUTO_CRL", ); ENUM(cert_validation_names, VALIDATION_GOOD, VALIDATION_REVOKED, diff --git a/src/libstrongswan/credentials/certificates/certificate.h b/src/libstrongswan/credentials/certificates/certificate.h index b7a88ffbd..d59126bd5 100644 --- a/src/libstrongswan/credentials/certificates/certificate.h +++ b/src/libstrongswan/credentials/certificates/certificate.h @@ -52,10 +52,6 @@ enum certificate_type_t { CERT_PKCS10_REQUEST, /** PGP certificate */ CERT_GPG, - /** Pluto cert_t (not a certificate_t), either x509 or PGP */ - CERT_PLUTO_CERT, - /** Pluto x509crl_t (not a certificate_t), certificate revocation list */ - CERT_PLUTO_CRL, }; /** diff --git a/src/libstrongswan/credentials/containers/container.c b/src/libstrongswan/credentials/containers/container.c index d1e67b21b..7456d43db 100644 --- a/src/libstrongswan/credentials/containers/container.c +++ b/src/libstrongswan/credentials/containers/container.c @@ -15,9 +15,11 @@ #include "container.h" -ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENVELOPED_DATA, +ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS12, "PKCS7", "PKCS7_DATA", "PKCS7_SIGNED_DATA", "PKCS7_ENVELOPED_DATA", + "PKCS7_ENCRYPTED_DATA", + "PKCS12", ); diff --git a/src/libstrongswan/credentials/containers/container.h b/src/libstrongswan/credentials/containers/container.h index fc5c09041..ee329881d 100644 --- a/src/libstrongswan/credentials/containers/container.h +++ b/src/libstrongswan/credentials/containers/container.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * @@ -31,14 +34,18 @@ typedef enum container_type_t container_type_t; * Type of the container. */ enum container_type_t { - /** Any kind of PKCS7/CMS container */ + /** Any kind of PKCS#7/CMS container */ CONTAINER_PKCS7, - /** PKCS7/CMS plain "data" */ + /** PKCS#7/CMS plain "data" */ CONTAINER_PKCS7_DATA, - /** PKCS7/CMS "signed-data" */ + /** PKCS#7/CMS "signed-data" */ CONTAINER_PKCS7_SIGNED_DATA, - /** PKCS7/CMS "enveloped-data" */ + /** PKCS#7/CMS "enveloped-data" */ CONTAINER_PKCS7_ENVELOPED_DATA, + /** PKCS#7/CMS "encrypted-data" */ + CONTAINER_PKCS7_ENCRYPTED_DATA, + /** A PKCS#12 container */ + CONTAINER_PKCS12, }; /** diff --git a/src/libstrongswan/credentials/containers/pkcs12.c b/src/libstrongswan/credentials/containers/pkcs12.c new file mode 100644 index 000000000..7b812d27d --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "pkcs12.h" + +#include + +/** + * v * ceiling(len/v) + */ +#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) + +/** + * Copy src to dst as many times as possible + */ +static inline void copy_chunk(chunk_t dst, chunk_t src) +{ + size_t i; + + for (i = 0; i < dst.len; i++) + { + dst.ptr[i] = src.ptr[i % src.len]; + } +} + +/** + * Treat two chunks as integers in network order and add them together. + * The result is stored in the first chunk, if the second chunk is longer or the + * result overflows this is ignored. + */ +static void add_chunks(chunk_t a, chunk_t b) +{ + u_int16_t sum; + u_int8_t rem = 0; + ssize_t i, j; + + for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) + { + sum = a.ptr[i] + b.ptr[j] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } + for (; i >= 0 && rem; i--) + { + sum = a.ptr[i] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } +} + +/** + * Do the actual key derivation with the given hasher, password and id. + */ +static bool derive_key(hash_algorithm_t hash, chunk_t unicode, chunk_t salt, + u_int64_t iterations, char id, chunk_t result) +{ + chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; + hasher_t *hasher; + size_t Slen, v, u; + u_int64_t i; + bool success = FALSE; + + hasher = lib->crypto->create_hasher(lib->crypto, hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, hash); + return FALSE; + } + switch (hash) + { + case HASH_MD2: + case HASH_MD5: + case HASH_SHA1: + case HASH_SHA224: + case HASH_SHA256: + v = 64; + break; + case HASH_SHA384: + case HASH_SHA512: + v = 128; + break; + default: + goto end; + } + u = hasher->get_hash_size(hasher); + + D = chunk_alloca(v); + memset(D.ptr, id, D.len); + + Slen = PKCS12_LEN(salt.len, v); + I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); + S = chunk_create(I.ptr, Slen); + P = chunk_create(I.ptr + Slen, I.len - Slen); + copy_chunk(S, salt); + copy_chunk(P, unicode); + + Ai = chunk_alloca(u); + B = chunk_alloca(v); + + while (TRUE) + { + if (!hasher->get_hash(hasher, D, NULL) || + !hasher->get_hash(hasher, I, Ai.ptr)) + { + goto end; + } + for (i = 1; i < iterations; i++) + { + if (!hasher->get_hash(hasher, Ai, Ai.ptr)) + { + goto end; + } + } + memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); + out = chunk_skip(out, Ai.len); + if (!out.len) + { + break; + } + copy_chunk(B, Ai); + /* B = B+1 */ + add_chunks(B, chunk_from_chars(0x01)); + Ij = chunk_create(I.ptr, v); + for (i = 0; i < I.len; i += v, Ij.ptr += v) + { /* Ij = Ij + B + 1 */ + add_chunks(Ij, B); + } + } + success = TRUE; +end: + hasher->destroy(hasher); + return success; +} + +/* + * Described in header + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key) +{ + chunk_t unicode = chunk_empty; + bool success; + int i; + + if (password.len) + { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ + unicode = chunk_alloca(password.len * 2 + 2); + for (i = 0; i < password.len; i++) + { + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = password.ptr[i]; + } + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = 0; + } + + success = derive_key(hash, unicode, salt, iterations, type, key); + memwipe(unicode.ptr, unicode.len); + return success; +} diff --git a/src/libstrongswan/credentials/containers/pkcs12.h b/src/libstrongswan/credentials/containers/pkcs12.h new file mode 100644 index 000000000..f22ef045a --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup pkcs12 pkcs12 + * @{ @ingroup containers + */ + +#ifndef PKCS12_H_ +#define PKCS12_H_ + +#include +#include + +typedef enum pkcs12_key_type_t pkcs12_key_type_t; +typedef struct pkcs12_t pkcs12_t; + +/** + * The types of password based keys used by PKCS#12. + */ +enum pkcs12_key_type_t { + PKCS12_KEY_ENCRYPTION = 1, + PKCS12_KEY_IV = 2, + PKCS12_KEY_MAC = 3, +}; + +/** + * PKCS#12/PFX container type. + */ +struct pkcs12_t { + + /** + * Implements container_t. + */ + container_t container; + + /** + * Create an enumerator over extracted certificates. + * + * @return enumerator over certificate_t + */ + enumerator_t* (*create_cert_enumerator)(pkcs12_t *this); + + /** + * Create an enumerator over extracted private keys. + * + * @return enumerator over private_key_t + */ + enumerator_t* (*create_key_enumerator)(pkcs12_t *this); +}; + +/** + * Derive the keys used in PKCS#12 for password integrity/privacy mode. + * + * @param hash hash algorithm to use for key derivation + * @param password password (ASCII) + * @param salt salt value + * @param iterations number of iterations + * @param type type of key to derive + * @param key the returned key, must be allocated of desired length + * @return TRUE on success + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key); + +#endif /** PKCS12_H_ @}*/ diff --git a/src/libstrongswan/crypto/crypters/crypter.c b/src/libstrongswan/crypto/crypters/crypter.c index 0730c707c..8123adde5 100644 --- a/src/libstrongswan/crypto/crypters/crypter.c +++ b/src/libstrongswan/crypto/crypters/crypter.c @@ -46,12 +46,13 @@ ENUM_NEXT(encryption_algorithm_names, ENCR_CAMELLIA_CBC, ENCR_CAMELLIA_CCM_ICV16 "CAMELLIA_CCM_8", "CAMELLIA_CCM_12", "CAMELLIA_CCM_16"); -ENUM_NEXT(encryption_algorithm_names, ENCR_UNDEFINED, ENCR_TWOFISH_CBC, ENCR_CAMELLIA_CCM_ICV16, +ENUM_NEXT(encryption_algorithm_names, ENCR_UNDEFINED, ENCR_RC2_CBC, ENCR_CAMELLIA_CCM_ICV16, "UNDEFINED", "DES_ECB", "SERPENT_CBC", - "TWOFISH_CBC"); -ENUM_END(encryption_algorithm_names, ENCR_TWOFISH_CBC); + "TWOFISH_CBC", + "RC2_CBC"); +ENUM_END(encryption_algorithm_names, ENCR_RC2_CBC); /* * Described in header. diff --git a/src/libstrongswan/crypto/crypters/crypter.h b/src/libstrongswan/crypto/crypters/crypter.h index fe854f53d..849aea500 100644 --- a/src/libstrongswan/crypto/crypters/crypter.h +++ b/src/libstrongswan/crypto/crypters/crypter.h @@ -60,7 +60,9 @@ enum encryption_algorithm_t { ENCR_UNDEFINED = 1024, ENCR_DES_ECB = 1025, ENCR_SERPENT_CBC = 1026, - ENCR_TWOFISH_CBC = 1027 + ENCR_TWOFISH_CBC = 1027, + /* see macros below to handle RC2 (effective) key length */ + ENCR_RC2_CBC = 1028, }; #define DES_BLOCK_SIZE 8 @@ -70,6 +72,15 @@ enum encryption_algorithm_t { #define SERPENT_BLOCK_SIZE 16 #define TWOFISH_BLOCK_SIZE 16 +/** + * For RC2, if the effective key size in bits is not key_size * 8, it should + * be encoded with the macro below. It can be decoded with the other two macros. + * After decoding the value should be validated. + */ +#define RC2_KEY_SIZE(kl, eff) ((kl) | ((eff) << 8)) +#define RC2_EFFECTIVE_KEY_LEN(ks) ((ks) >> 8) +#define RC2_KEY_LEN(ks) ((ks) & 0xff) + /** * enum name for encryption_algorithm_t. */ diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 12db0961b..5a0dccced 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -265,7 +265,10 @@ METHOD(crypto_tester_t, test_crypter, bool, failure: crypter->destroy(crypter); chunk_free(&cipher); - chunk_free(&plain); + if (plain.ptr != vector->plain) + { + chunk_free(&plain); + } if (failed) { DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed", diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index dc73d5223..679bb324e 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -141,6 +141,9 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, case AUTH_HMAC_SHA2_384_384: *length = 48; break; + case AUTH_HMAC_SHA2_512_512: + *length = 64; + break; default: break; } @@ -163,6 +166,7 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, case AUTH_HMAC_SHA2_384_384: return HASH_SHA384; case AUTH_HMAC_SHA2_512_256: + case AUTH_HMAC_SHA2_512_512: return HASH_SHA512; case AUTH_AES_CMAC_96: case AUTH_AES_128_GMAC: @@ -177,6 +181,74 @@ hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, return HASH_UNKNOWN; } +/* + * Described in header. + */ +integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, + size_t length) +{ + switch (alg) + { + case HASH_MD5: + switch (length) + { + case 12: + return AUTH_HMAC_MD5_96; + case 16: + return AUTH_HMAC_MD5_128; + } + break; + case HASH_SHA1: + case HASH_PREFERRED: + switch (length) + { + case 12: + return AUTH_HMAC_SHA1_96; + case 16: + return AUTH_HMAC_SHA1_128; + case 20: + return AUTH_HMAC_SHA1_160; + } + break; + case HASH_SHA256: + switch (length) + { + case 12: + return AUTH_HMAC_SHA2_256_96; + case 16: + return AUTH_HMAC_SHA2_256_128; + case 32: + return AUTH_HMAC_SHA2_256_256; + } + break; + case HASH_SHA384: + switch (length) + { + case 24: + return AUTH_HMAC_SHA2_384_192; + case 48: + return AUTH_HMAC_SHA2_384_384; + + } + break; + case HASH_SHA512: + switch (length) + { + case 32: + return AUTH_HMAC_SHA2_512_256; + case 64: + return AUTH_HMAC_SHA2_512_512; + } + break; + case HASH_MD2: + case HASH_MD4: + case HASH_SHA224: + case HASH_UNKNOWN: + break; + } + return AUTH_UNDEFINED; +} + /* * Described in header. */ diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h index 759f6a23c..4e46fca10 100644 --- a/src/libstrongswan/crypto/hashers/hasher.h +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -153,6 +153,17 @@ hash_algorithm_t hasher_algorithm_from_prf(pseudo_random_function_t alg); hash_algorithm_t hasher_algorithm_from_integrity(integrity_algorithm_t alg, size_t *length); +/** + * Conversion of hash algorithm to integrity algorithm (if based on a hash). + * + * @param alg hash algorithm + * @param length length of the signature + * @return integrity algorithm, AUTH_UNDEFINED if none is known + * based on the given hash function + */ +integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, + size_t length); + /** * Conversion of hash algorithm into ASN.1 OID. * diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c new file mode 100644 index 000000000..679d270d2 --- /dev/null +++ b/src/libstrongswan/crypto/pkcs5.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2012-2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "pkcs5.h" + +#include +#include +#include +#include +#include + +typedef struct private_pkcs5_t private_pkcs5_t; + +/** + * Private data of a pkcs5_t object + */ +struct private_pkcs5_t { + + /** + * Implements pkcs5_t. + */ + pkcs5_t public; + + /** + * Salt used during encryption + */ + chunk_t salt; + + /** + * Iterations for key derivation + */ + u_int64_t iterations; + + /** + * Encryption algorithm + */ + encryption_algorithm_t encr; + + /** + * Encryption key length + */ + size_t keylen; + + /** + * Crypter + */ + crypter_t *crypter; + + + /** + * The encryption scheme + */ + enum { + PKCS5_SCHEME_PBES1, + PKCS5_SCHEME_PBES2, + PKCS5_SCHEME_PKCS12, + } scheme; + + /** + * Data used for individual schemes + */ + union { + struct { + /** + * Hash algorithm + */ + hash_algorithm_t hash; + + /** + * Hasher + */ + hasher_t *hasher; + + } pbes1; + struct { + /** + * PRF algorithm + */ + pseudo_random_function_t prf_alg; + + /** + * PRF + */ + prf_t * prf; + + /** + * IV + */ + chunk_t iv; + + } pbes2; + } data; +}; + +/** + * Verify padding of decrypted blob. + * Length of blob is adjusted accordingly. + */ +static bool verify_padding(chunk_t *blob) +{ + u_int8_t padding, count; + + padding = count = blob->ptr[blob->len - 1]; + + if (padding > 8) + { + return FALSE; + } + for (; blob->len && count; --blob->len, --count) + { + if (blob->ptr[blob->len - 1] != padding) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Prototype for key derivation functions. + */ +typedef bool (*kdf_t)(private_pkcs5_t *this, chunk_t password, chunk_t key); + +/** + * Try to decrypt the given data with the given password using the given + * key derivation function. keymat is where the kdf function writes the key + * to, key and iv point to the actual keys and initialization vectors resp. + */ +static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, + chunk_t data, chunk_t *decrypted, kdf_t kdf, + chunk_t keymat, chunk_t key, chunk_t iv) +{ + if (!kdf(this, password, keymat)) + { + return FALSE; + } + if (!this->crypter->set_key(this->crypter, key) || + !this->crypter->decrypt(this->crypter, data, iv, decrypted)) + { + memwipe(keymat.ptr, keymat.len); + return FALSE; + } + memwipe(keymat.ptr, keymat.len); + if (verify_padding(decrypted)) + { + return TRUE; + } + chunk_free(decrypted); + return FALSE; +} + +/** + * KDF as used by PKCS#12 + */ +static bool pkcs12_kdf(private_pkcs5_t *this, chunk_t password, chunk_t keymat) +{ + chunk_t key, iv; + + key = chunk_create(keymat.ptr, this->keylen); + iv = chunk_create(keymat.ptr + this->keylen, keymat.len - this->keylen); + + return pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_ENCRYPTION, key) && + pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_IV, iv); +} + +/** + * Function F of PBKDF2 + */ +static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed, + u_int64_t iterations) +{ + chunk_t u; + u_int64_t i; + + u = chunk_alloca(prf->get_block_size(prf)); + if (!prf->get_bytes(prf, seed, u.ptr)) + { + return FALSE; + } + memcpy(block.ptr, u.ptr, block.len); + + for (i = 1; i < iterations; i++) + { + if (!prf->get_bytes(prf, u, u.ptr)) + { + return FALSE; + } + memxor(block.ptr, u.ptr, block.len); + } + return TRUE; +} + +/** + * PBKDF2 key derivation function for PBES2, key must be allocated + */ +static bool pbkdf2(private_pkcs5_t *this, chunk_t password, chunk_t key) +{ + prf_t *prf; + chunk_t keymat, block, seed; + size_t blocks; + u_int32_t i = 0; + + prf = this->data.pbes2.prf; + + if (!prf->set_key(prf, password)) + { + return FALSE; + } + + block.len = prf->get_block_size(prf); + blocks = (key.len - 1) / block.len + 1; + keymat = chunk_alloca(blocks * block.len); + + seed = chunk_cata("cc", this->salt, chunk_from_thing(i)); + + for (; i < blocks; i++) + { + htoun32(seed.ptr + this->salt.len, i + 1); + block.ptr = keymat.ptr + (i * block.len); + if (!pbkdf2_f(block, prf, seed, this->iterations)) + { + return FALSE; + } + } + memcpy(key.ptr, keymat.ptr, key.len); + return TRUE; +} + +/** + * PBKDF1 key derivation function for PBES1, key must be allocated + */ +static bool pbkdf1(private_pkcs5_t *this, chunk_t password, chunk_t key) +{ + hasher_t *hasher; + chunk_t hash; + u_int64_t i; + + hasher = this->data.pbes1.hasher; + + hash = chunk_alloca(hasher->get_hash_size(hasher)); + if (!hasher->get_hash(hasher, password, NULL) || + !hasher->get_hash(hasher, this->salt, hash.ptr)) + { + return FALSE; + } + + for (i = 1; i < this->iterations; i++) + { + if (!hasher->get_hash(hasher, hash, hash.ptr)) + { + return FALSE; + } + } + memcpy(key.ptr, hash.ptr, key.len); + return TRUE; +} + +static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) +{ + if (!this->crypter) + { + this->crypter = lib->crypto->create_crypter(lib->crypto, this->encr, + this->keylen); + if (!this->crypter) + { + DBG1(DBG_ASN, " %N encryption algorithm not available", + encryption_algorithm_names, this->encr); + return FALSE; + } + } + if (data.len % this->crypter->get_block_size(this->crypter)) + { + DBG1(DBG_ASN, " data size is not a multiple of block size"); + return FALSE; + } + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + { + if (!this->data.pbes1.hasher) + { + hasher_t *hasher; + + hasher = lib->crypto->create_hasher(lib->crypto, + this->data.pbes1.hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, this->data.pbes1.hash); + return FALSE; + } + if (hasher->get_hash_size(hasher) < this->keylen) + { + hasher->destroy(hasher); + return FALSE; + } + this->data.pbes1.hasher = hasher; + } + } + case PKCS5_SCHEME_PBES2: + { + if (!this->data.pbes2.prf) + { + prf_t *prf; + + prf = lib->crypto->create_prf(lib->crypto, + this->data.pbes2.prf_alg); + if (!prf) + { + DBG1(DBG_ASN, " %N prf algorithm not available", + pseudo_random_function_names, + this->data.pbes2.prf_alg); + return FALSE; + } + this->data.pbes2.prf = prf; + } + } + case PKCS5_SCHEME_PKCS12: + break; + } + return TRUE; +} + +METHOD(pkcs5_t, decrypt, bool, + private_pkcs5_t *this, chunk_t password, chunk_t data, chunk_t *decrypted) +{ + chunk_t keymat, key, iv; + kdf_t kdf; + + if (!ensure_crypto_primitives(this, data) || !decrypted) + { + return FALSE; + } + kdf = pbkdf1; + switch (this->scheme) + { + case PKCS5_SCHEME_PKCS12: + kdf = pkcs12_kdf; + /* fall-through */ + case PKCS5_SCHEME_PBES1: + keymat = chunk_alloca(this->keylen + + this->crypter->get_iv_size(this->crypter)); + key = chunk_create(keymat.ptr, this->keylen); + iv = chunk_create(keymat.ptr + this->keylen, + keymat.len - this->keylen); + break; + case PKCS5_SCHEME_PBES2: + kdf = pbkdf2; + keymat = chunk_alloca(this->keylen); + key = keymat; + iv = this->data.pbes2.iv; + break; + default: + return FALSE; + } + return decrypt_generic(this, password, data, decrypted, kdf, + keymat, key, iv); +} + +/** + * ASN.1 definition of a PBEParameter structure + */ +static const asn1Object_t pbeParameterObjects[] = { + { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBEPARAM_SALT 1 +#define PBEPARAM_ITERATION_COUNT 2 + +/** + * Parse a PBEParameter structure + */ +static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(pbeParameterObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBEPARAM_SALT: + { + this->salt = chunk_clone(object); + break; + } + case PBEPARAM_ITERATION_COUNT: + { + this->iterations = asn1_parse_integer_uint64(object); + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PBKDF2-params structure + * The salt is actually a CHOICE and could be an AlgorithmIdentifier from + * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions). + */ +static const asn1Object_t pbkdf2ParamsObjects[] = { + { 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBKDF2_SALT 1 +#define PBKDF2_ITERATION_COUNT 2 +#define PBKDF2_KEYLENGTH 3 +#define PBKDF2_PRF 5 + +/** + * Parse a PBKDF2-params structure + */ +static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(pbkdf2ParamsObjects, blob); + parser->set_top_level(parser, level0); + + /* keylen is optional */ + this->keylen = 0; + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBKDF2_SALT: + { + this->salt = chunk_clone(object); + break; + } + case PBKDF2_ITERATION_COUNT: + { + this->iterations = asn1_parse_integer_uint64(object); + break; + } + case PBKDF2_KEYLENGTH: + { + this->keylen = (size_t)asn1_parse_integer_uint64(object); + break; + } + case PBKDF2_PRF: + { /* defaults to id-hmacWithSHA1, no other is currently defined */ + this->data.pbes2.prf_alg = PRF_HMAC_SHA1; + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PBES2-params structure + */ +static const asn1Object_t pbes2ParamsObjects[] = { + { 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBES2PARAMS_KEY_DERIVATION_FUNC 1 +#define PBES2PARAMS_ENCRYPTION_SCHEME 2 + +/** + * Parse a PBES2-params structure + */ +static bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0) +{ + asn1_parser_t *parser; + chunk_t object, params; + int objectID; + bool success = FALSE; + + parser = asn1_parser_create(pbes2ParamsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBES2PARAMS_KEY_DERIVATION_FUNC: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_PBKDF2) + { /* unsupported key derivation function */ + goto end; + } + if (!parse_pbkdf2_params(this, params, + parser->get_level(parser) + 1)) + { + goto end; + } + break; + } + case PBES2PARAMS_ENCRYPTION_SCHEME: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_3DES_EDE_CBC) + { /* unsupported encryption scheme */ + goto end; + } + if (this->keylen <= 0) + { /* default key length for DES-EDE3-CBC-Pad */ + this->keylen = 24; + } + if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING, + parser->get_level(parser) + 1, "IV")) + { + goto end; + } + this->encr = ENCR_3DES; + this->data.pbes2.iv = chunk_clone(params); + break; + } + } + } + success = parser->success(parser); +end: + parser->destroy(parser); + return success; +} + +METHOD(pkcs5_t, destroy, void, + private_pkcs5_t *this) +{ + DESTROY_IF(this->crypter); + chunk_free(&this->salt); + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + DESTROY_IF(this->data.pbes1.hasher); + break; + case PKCS5_SCHEME_PBES2: + DESTROY_IF(this->data.pbes2.prf); + chunk_free(&this->data.pbes2.iv); + break; + case PKCS5_SCHEME_PKCS12: + break; + } + free(this); +} + +/* + * Described in header + */ +pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0) +{ + private_pkcs5_t *this; + chunk_t params; + int oid; + + INIT(this, + .public = { + .decrypt = _decrypt, + .destroy = _destroy, + }, + .scheme = PKCS5_SCHEME_PBES1, + .keylen = 8, + ); + + oid = asn1_parse_algorithmIdentifier(blob, level0, ¶ms); + + switch (oid) + { + case OID_PBE_MD5_DES_CBC: + this->encr = ENCR_DES; + this->data.pbes1.hash = HASH_MD5; + break; + case OID_PBE_SHA1_DES_CBC: + this->encr = ENCR_DES; + this->data.pbes1.hash = HASH_SHA1; + break; + case OID_PBE_SHA1_3DES_CBC: + this->scheme = PKCS5_SCHEME_PKCS12; + this->keylen = 24; + this->encr = ENCR_3DES; + this->data.pbes1.hash = HASH_SHA1; + break; + case OID_PBE_SHA1_RC2_CBC_40: + case OID_PBE_SHA1_RC2_CBC_128: + this->scheme = PKCS5_SCHEME_PKCS12; + this->keylen = (oid == OID_PBE_SHA1_RC2_CBC_40) ? 5 : 16; + this->encr = ENCR_RC2_CBC; + this->data.pbes1.hash = HASH_SHA1; + break; + case OID_PBES2: + this->scheme = PKCS5_SCHEME_PBES2; + break; + default: + /* encryption scheme not supported */ + goto failure; + } + + switch (this->scheme) + { + case PKCS5_SCHEME_PBES1: + case PKCS5_SCHEME_PKCS12: + if (!parse_pbes1_params(this, params, level0)) + { + goto failure; + } + break; + case PKCS5_SCHEME_PBES2: + if (!parse_pbes2_params(this, params, level0)) + { + goto failure; + } + break; + } + return &this->public; + +failure: + destroy(this); + return NULL; +} diff --git a/src/libstrongswan/crypto/pkcs5.h b/src/libstrongswan/crypto/pkcs5.h new file mode 100644 index 000000000..b16d3736e --- /dev/null +++ b/src/libstrongswan/crypto/pkcs5.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup pkcs5 pkcs5 + * @{ @ingroup crypto + */ + +#ifndef PKCS5_H_ +#define PKCS5_H_ + +typedef struct pkcs5_t pkcs5_t; + +#include + +/** + * PKCS#5 helper class + */ +struct pkcs5_t { + + /** + * Decrypt the given data using the given password and the scheme derived + * from the initial AlgorithmIdentifier object. + * + * @param password password used for decryption + * @param data data to decrypt + * @param decrypted decrypted data gets allocated + * @return TRUE on success, FALSE otherwise + */ + bool (*decrypt)(pkcs5_t *this, chunk_t password, chunk_t data, + chunk_t *decrypted) __attribute__((warn_unused_result)); + + /** + * Destroy the object and any associated cryptographic primitive. + */ + void (*destroy)(pkcs5_t *this); +}; + +/** + * Create a PKCS#5 helper object from an ASN.1 encoded AlgorithmIdentifier + * object. + * + * @param blob ASN.1 encoded AlgorithmIdentifier + * @param level0 ASN.1 parser level + * @return pkcs5_t object, NULL on failure + */ +pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0); + +#endif /** PKCS5_H_ @}*/ diff --git a/src/libstrongswan/crypto/signers/signer.h b/src/libstrongswan/crypto/signers/signer.h index 9b6bd479a..e0cf7eb5a 100644 --- a/src/libstrongswan/crypto/signers/signer.h +++ b/src/libstrongswan/crypto/signers/signer.h @@ -70,8 +70,10 @@ enum integrity_algorithm_t { AUTH_HMAC_SHA2_256_256 = 1027, /** SHA384 full length truncation variant, as used in TLS */ AUTH_HMAC_SHA2_384_384 = 1028, + /** SHA512 full length truncation variant */ + AUTH_HMAC_SHA2_512_512 = 1029, /** draft-kanno-ipsecme-camellia-xcbc, not yet assigned by IANA */ - AUTH_CAMELLIA_XCBC_96 = 1029, + AUTH_CAMELLIA_XCBC_96 = 1030, }; /** diff --git a/src/libstrongswan/plugins/af_alg/af_alg_signer.c b/src/libstrongswan/plugins/af_alg/af_alg_signer.c index d995b1351..6ee380633 100644 --- a/src/libstrongswan/plugins/af_alg/af_alg_signer.c +++ b/src/libstrongswan/plugins/af_alg/af_alg_signer.c @@ -64,6 +64,7 @@ static struct { {AUTH_HMAC_SHA2_384_192, "hmac(sha384)", 24, 48, }, {AUTH_HMAC_SHA2_384_384, "hmac(sha384)", 48, 48, }, {AUTH_HMAC_SHA2_512_256, "hmac(sha512)", 32, 64, }, + {AUTH_HMAC_SHA2_512_512, "hmac(sha512)", 64, 64, }, {AUTH_AES_XCBC_96, "xcbc(aes)", 12, 16, }, {AUTH_CAMELLIA_XCBC_96, "xcbc(camellia)", 12, 16, }, }; diff --git a/src/libstrongswan/plugins/hmac/hmac_plugin.c b/src/libstrongswan/plugins/hmac/hmac_plugin.c index f9c0c484b..43d5a0364 100644 --- a/src/libstrongswan/plugins/hmac/hmac_plugin.c +++ b/src/libstrongswan/plugins/hmac/hmac_plugin.c @@ -73,6 +73,8 @@ METHOD(plugin_t, get_features, int, PLUGIN_DEPENDS(HASHER, HASH_SHA384), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), PLUGIN_DEPENDS(HASHER, HASH_SHA512), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), + PLUGIN_DEPENDS(HASHER, HASH_SHA512), }; *features = f; return countof(f); diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 0ca27983f..7ae5ea43e 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -24,6 +24,7 @@ libstrongswan_openssl_la_SOURCES = \ openssl_x509.c openssl_x509.h \ openssl_crl.c openssl_crl.h \ openssl_pkcs7.c openssl_pkcs7.h \ + openssl_pkcs12.c openssl_pkcs12.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h \ openssl_gcm.c openssl_gcm.h diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs12.c b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c new file mode 100644 index 000000000..d16b2cc05 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs12.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +#define _GNU_SOURCE /* for asprintf() */ +#include +#include + +#include "openssl_pkcs12.h" +#include "openssl_util.h" + +#include +#include + +typedef struct private_pkcs12_t private_pkcs12_t; + +/** + * Private data of a pkcs12_t object. + */ +struct private_pkcs12_t { + + /** + * Public pkcs12_t interface. + */ + pkcs12_t public; + + /** + * OpenSSL PKCS#12 structure + */ + PKCS12 *p12; + + /** + * Credentials contained in container + */ + mem_cred_t *creds; +}; + +/** + * Decode certificate and add it to our credential set + */ +static bool add_cert(private_pkcs12_t *this, X509 *x509) +{ + certificate_t *cert = NULL; + chunk_t encoding; + + if (!x509) + { /* no certificate is ok */ + return TRUE; + } + encoding = openssl_i2chunk(X509, x509); + if (encoding.ptr) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, encoding, + BUILD_END); + if (cert) + { + this->creds->add_cert(this->creds, FALSE, cert); + } + } + chunk_free(&encoding); + X509_free(x509); + return cert != NULL; +} + +/** + * Add CA certificates to our credential set + */ +static bool add_cas(private_pkcs12_t *this, STACK_OF(X509) *cas) +{ + bool success = TRUE; + int i; + + if (!cas) + { /* no CAs is ok */ + return TRUE; + } + for (i = 0; i < sk_X509_num(cas); i++) + { + if (!add_cert(this, sk_X509_value(cas, i))) + { /* continue to free all X509 objects */ + success = FALSE; + } + } + sk_X509_free(cas); + return success; +} + +/** + * Decode private key and add it to our credential set + */ +static bool add_key(private_pkcs12_t *this, EVP_PKEY *private) +{ + private_key_t *key = NULL; + chunk_t encoding; + key_type_t type; + + if (!private) + { /* no private key is ok */ + return TRUE; + } + switch (EVP_PKEY_type(private->type)) + { + case EVP_PKEY_RSA: + type = KEY_RSA; + break; + case EVP_PKEY_EC: + type = KEY_ECDSA; + break; + default: + EVP_PKEY_free(private); + return FALSE; + } + encoding = openssl_i2chunk(PrivateKey, private); + if (encoding.ptr) + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, + BUILD_BLOB_ASN1_DER, encoding, + BUILD_END); + if (key) + { + this->creds->add_key(this->creds, key); + } + } + chunk_clear(&encoding); + EVP_PKEY_free(private); + return key != NULL; +} + +/** + * Decrypt PKCS#12 file and unpack credentials + */ +static bool decrypt_and_unpack(private_pkcs12_t *this) +{ + enumerator_t *enumerator; + shared_key_t *shared; + STACK_OF(X509) *cas = NULL; + EVP_PKEY *private; + X509 *cert; + chunk_t key; + char *password; + bool success = FALSE; + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + key = shared->get_key(shared); + if (!key.ptr || asprintf(&password, "%.*s", (int)key.len, key.ptr) < 0) + { + password = NULL; + } + if (PKCS12_parse(this->p12, password, &private, &cert, &cas)) + { + success = add_key(this, private); + success &= add_cert(this, cert); + success &= add_cas(this, cas); + free(password); + break; + } + free(password); + } + enumerator->destroy(enumerator); + return success; +} + +METHOD(container_t, get_type, container_type_t, + private_pkcs12_t *this) +{ + return CONTAINER_PKCS12; +} + +METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY, + KEY_ANY, NULL, FALSE); +} + +METHOD(pkcs12_t, create_key_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_private_enumerator(&this->creds->set, + KEY_ANY, NULL); +} + +METHOD(container_t, destroy, void, + private_pkcs12_t *this) +{ + if (this->p12) + { + PKCS12_free(this->p12); + } + this->creds->destroy(this->creds); + free(this); +} + +/** + * Parse a PKCS#12 container + */ +static pkcs12_t *parse(chunk_t blob) +{ + private_pkcs12_t *this; + BIO *bio; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = (void*)return_false, + .get_encoding = (void*)return_false, + .destroy = _destroy, + }, + .create_cert_enumerator = _create_cert_enumerator, + .create_key_enumerator = _create_key_enumerator, + }, + .creds = mem_cred_create(), + ); + + bio = BIO_new_mem_buf(blob.ptr, blob.len); + this->p12 = d2i_PKCS12_bio(bio, NULL); + BIO_free(bio); + + if (!this->p12 || !decrypt_and_unpack(this)) + { + destroy(this); + return NULL; + } + return &this->public; +} + +/* + * Defined in header + */ +pkcs12_t *openssl_pkcs12_load(container_type_t type, va_list args) +{ + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + return blob.len ? parse(blob) : NULL; +} diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs12.h b/src/libstrongswan/plugins/openssl/openssl_pkcs12.h new file mode 100644 index 000000000..5c3e5933d --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs12.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup openssl_pkcs12 openssl_pkcs12 + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_PKCS12_H_ +#define OPENSSL_PKCS12_H_ + +#include + +/** + * Load a PKCS#12 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS12 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs12_t *openssl_pkcs12_load(container_type_t type, va_list args); + +#endif /** OPENSSL_PKCS12_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index fb7a6d587..5d2074144 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -14,6 +14,7 @@ * for more details. */ +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include "openssl_util.h" #include "openssl_crypter.h" #include "openssl_hasher.h" @@ -41,6 +43,7 @@ #include "openssl_x509.h" #include "openssl_crl.h" #include "openssl_pkcs7.h" +#include "openssl_pkcs12.h" #include "openssl_rng.h" #include "openssl_hmac.h" #include "openssl_gcm.h" @@ -130,16 +133,53 @@ static void destroy_function(struct CRYPTO_dynlock_value *lock, free(lock); } +/** + * Thread-local value used to cleanup thread-specific error buffers + */ +static thread_value_t *cleanup; + +/** + * Called when a thread is destroyed. Avoid recursion by setting the thread id + * explicitly. + */ +static void cleanup_thread(void *arg) +{ +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL + CRYPTO_THREADID tid; + + CRYPTO_THREADID_set_numeric(&tid, (u_long)(uintptr_t)arg); + ERR_remove_thread_state(&tid); +#else + ERR_remove_state((u_long)(uintptr_t)arg); +#endif +} + /** * Thread-ID callback function */ -static unsigned long id_function(void) +static u_long id_function(void) { + u_long id; + /* ensure the thread ID is never zero, otherwise OpenSSL might try to * acquire locks recursively */ - return 1 + (unsigned long)thread_current_id(); + id = 1 + (u_long)thread_current_id(); + + /* cleanup a thread's state later if OpenSSL interacted with it */ + cleanup->set(cleanup, (void*)(uintptr_t)id); + return id; } +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL +/** + * Callback for thread ID + */ +static void threadid_function(CRYPTO_THREADID *threadid) +{ + CRYPTO_THREADID_set_numeric(threadid, id_function()); +} +#endif /* OPENSSL_VERSION_NUMBER */ + /** * initialize OpenSSL for multi-threaded use */ @@ -147,7 +187,14 @@ static void threading_init() { int i, num_locks; + cleanup = thread_value_create(cleanup_thread); + +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL + CRYPTO_THREADID_set_callback(threadid_function); +#else CRYPTO_set_id_callback(id_function); +#endif + CRYPTO_set_locking_callback(locking_function); CRYPTO_set_dynlock_create_callback(create_function); @@ -162,6 +209,24 @@ static void threading_init() } } +/** + * cleanup OpenSSL threading locks + */ +static void threading_cleanup() +{ + int i, num_locks; + + num_locks = CRYPTO_num_locks(); + for (i = 0; i < num_locks; i++) + { + mutex[i]->destroy(mutex[i]); + } + free(mutex); + mutex = NULL; + + cleanup->destroy(cleanup); +} + /** * Seed the OpenSSL RNG, if required */ @@ -191,22 +256,6 @@ static bool seed_rng() return TRUE; } -/** - * cleanup OpenSSL threading locks - */ -static void threading_cleanup() -{ - int i, num_locks; - - num_locks = CRYPTO_num_locks(); - for (i = 0; i < num_locks; i++) - { - mutex[i]->destroy(mutex[i]); - } - free(mutex); - mutex = NULL; -} - METHOD(plugin_t, get_name, char*, private_openssl_plugin_t *this) { @@ -307,6 +356,7 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_192), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_384), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), #endif #endif /* OPENSSL_NO_HMAC */ #if OPENSSL_VERSION_NUMBER >= 0x1000100fL @@ -392,6 +442,8 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS7), #endif /* OPENSSL_NO_CMS */ #endif /* OPENSSL_VERSION_NUMBER */ + PLUGIN_REGISTER(CONTAINER_DECODE, openssl_pkcs12_load, TRUE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), #ifndef OPENSSL_NO_ECDH /* EC DH groups */ PLUGIN_REGISTER(DH, openssl_ec_diffie_hellman_create), @@ -444,13 +496,15 @@ METHOD(plugin_t, get_features, int, METHOD(plugin_t, destroy, void, private_openssl_plugin_t *this) { + CONF_modules_free(); + OBJ_cleanup(); + EVP_cleanup(); #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ - EVP_cleanup(); - CONF_modules_free(); - + CRYPTO_cleanup_all_ex_data(); threading_cleanup(); + ERR_free_strings(); free(this); } diff --git a/src/libstrongswan/plugins/pem/pem_builder.c b/src/libstrongswan/plugins/pem/pem_builder.c index 08e81b3c5..e9d55f3b8 100644 --- a/src/libstrongswan/plugins/pem/pem_builder.c +++ b/src/libstrongswan/plugins/pem/pem_builder.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Copyright (C) 2001-2008 Andreas Steffen * Hochschule fuer Technik Rapperswil @@ -564,3 +565,10 @@ certificate_t *pem_certificate_load(certificate_type_t type, va_list args) return pem_load(CRED_CERTIFICATE, type, args); } +/** + * Container PEM loader. + */ +container_t *pem_container_load(container_type_t type, va_list args) +{ + return pem_load(CRED_CONTAINER, type, args); +} diff --git a/src/libstrongswan/plugins/pem/pem_builder.h b/src/libstrongswan/plugins/pem/pem_builder.h index 87f5a2c69..b1bfc6d4d 100644 --- a/src/libstrongswan/plugins/pem/pem_builder.h +++ b/src/libstrongswan/plugins/pem/pem_builder.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -25,6 +26,7 @@ #include #include #include +#include /** * Load PEM encoded private keys. @@ -53,5 +55,14 @@ public_key_t *pem_public_key_load(key_type_t type, va_list args); */ certificate_t *pem_certificate_load(certificate_type_t type, va_list args); +/** + * Build PEM encoded containers. + * + * @param type type of the container + * @param args builder_part_t argument list + * @return container, NULL if failed + */ +container_t *pem_container_load(container_type_t type, va_list args); + #endif /** PEM_BUILDER_H_ @}*/ diff --git a/src/libstrongswan/plugins/pem/pem_plugin.c b/src/libstrongswan/plugins/pem/pem_plugin.c index d74ab9ee3..e7edd7b89 100644 --- a/src/libstrongswan/plugins/pem/pem_plugin.c +++ b/src/libstrongswan/plugins/pem/pem_plugin.c @@ -105,11 +105,10 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(CERT_DECODE, CERT_GPG), PLUGIN_DEPENDS(CERT_DECODE, CERT_GPG), - /* pluto specific certificate formats */ - PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), - PLUGIN_PROVIDE(CERT_DECODE, CERT_PLUTO_CERT), - PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE), - PLUGIN_PROVIDE(CERT_DECODE, CERT_PLUTO_CRL), + /* container PEM decoding */ + PLUGIN_REGISTER(CONTAINER_DECODE, pem_container_load, FALSE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), + PLUGIN_DEPENDS(CONTAINER_DECODE, CONTAINER_PKCS12), }; *features = f; return countof(f); diff --git a/src/libstrongswan/plugins/pkcs12/Makefile.am b/src/libstrongswan/plugins/pkcs12/Makefile.am new file mode 100644 index 000000000..1f703bd6c --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-pkcs12.la +else +plugin_LTLIBRARIES = libstrongswan-pkcs12.la +endif + +libstrongswan_pkcs12_la_SOURCES = \ + pkcs12_plugin.h pkcs12_plugin.c \ + pkcs12_decode.h pkcs12_decode.c + +libstrongswan_pkcs12_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c new file mode 100644 index 000000000..379f24796 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "pkcs12_decode.h" + +#include +#include +#include +#include +#include + +typedef struct private_pkcs12_t private_pkcs12_t; + +/** + * Private data of a pkcs12_t object + */ +struct private_pkcs12_t { + + /** + * Public interface + */ + pkcs12_t public; + + /** + * Contained credentials + */ + mem_cred_t *creds; +}; + +METHOD(container_t, get_type, container_type_t, + private_pkcs12_t *this) +{ + return CONTAINER_PKCS12; +} + +METHOD(container_t, get_data, bool, + private_pkcs12_t *this, chunk_t *data) +{ + /* we could return the content of the outer-most PKCS#7 container (authSafe) + * don't really see the point though */ + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs12_t *this, chunk_t *encoding) +{ + /* similar to get_data() we don't have any use for it at the moment */ + return FALSE; +} + +METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY, + KEY_ANY, NULL, FALSE); +} + +METHOD(pkcs12_t, create_key_enumerator, enumerator_t*, + private_pkcs12_t *this) +{ + return this->creds->set.create_private_enumerator(&this->creds->set, + KEY_ANY, NULL); +} + +METHOD(container_t, destroy, void, + private_pkcs12_t *this) +{ + this->creds->destroy(this->creds); + free(this); +} + +static private_pkcs12_t *pkcs12_create() +{ + private_pkcs12_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .create_cert_enumerator = _create_cert_enumerator, + .create_key_enumerator = _create_key_enumerator, + }, + .creds = mem_cred_create(), + ); + return this; +} + +/** + * ASN.1 definition of an CertBag structure + */ +static const asn1Object_t certBagObjects[] = { + { 0, "CertBag", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ + { 1, "certId", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "certValue", ASN1_CONTEXT_C_0, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define CERT_BAG_ID 1 +#define CERT_BAG_VALUE 2 + +/** + * Parse a CertBag structure and extract certificate + */ +static bool add_certificate(private_pkcs12_t *this, int level0, chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + int oid = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(certBagObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case CERT_BAG_ID: + oid = asn1_known_oid(object); + break; + case CERT_BAG_VALUE: + { + if (oid == OID_X509_CERTIFICATE && + asn1_parse_simple_object(&object, ASN1_OCTET_STRING, + parser->get_level(parser)+1, "x509Certificate")) + { + certificate_t *cert; + + DBG2(DBG_ASN, "-- > parsing certificate from PKCS#12"); + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, object, + BUILD_END); + if (cert) + { + this->creds->add_cert(this->creds, FALSE, cert); + DBG2(DBG_ASN, "-- < --"); + } + else + { + DBG2(DBG_ASN, "-- < failed parsing certificate from " + "PKCS#12"); + } + } + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of an AuthenticatedSafe structure + */ +static const asn1Object_t safeContentsObjects[] = { + { 0, "SafeContents", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "SafeBag", ASN1_SEQUENCE, ASN1_BODY }, /* 1 */ + { 2, "bagId", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "bagValue", ASN1_CONTEXT_C_0, ASN1_BODY }, /* 3 */ + { 2, "bagAttr", ASN1_SET, ASN1_OPT|ASN1_RAW }, /* 4 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define SAFE_BAG_ID 2 +#define SAFE_BAG_VALUE 3 + +/** + * Parse a SafeContents structure and extract credentials + */ +static bool parse_safe_contents(private_pkcs12_t *this, int level0, + chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + int oid = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(safeContentsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case SAFE_BAG_ID: + oid = asn1_known_oid(object); + break; + case SAFE_BAG_VALUE: + { + switch (oid) + { + case OID_P12_CERT_BAG: + { + add_certificate(this, parser->get_level(parser)+1, + object); + break; + } + case OID_P12_KEY_BAG: + case OID_P12_PKCS8_KEY_BAG: + { + private_key_t *key; + + DBG2(DBG_ASN, "-- > parsing private key from PKCS#12"); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + KEY_ANY, BUILD_BLOB_ASN1_DER, object, + BUILD_END); + if (key) + { + this->creds->add_key(this->creds, key); + DBG2(DBG_ASN, "-- < --"); + } + else + { + DBG2(DBG_ASN, "-- < failed parsing private key " + "from PKCS#12"); + } + } + default: + break; + } + break; + } + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of an AuthenticatedSafe structure + */ +static const asn1Object_t authenticatedSafeObjects[] = { + { 0, "AuthenticatedSafe", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "ContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define AUTHENTICATED_SAFE_DATA 1 + +/** + * Parse an AuthenticatedSafe structure + */ +static bool parse_authenticated_safe(private_pkcs12_t *this, chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success = FALSE; + + parser = asn1_parser_create(authenticatedSafeObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case AUTHENTICATED_SAFE_DATA: + { + container_t *container; + chunk_t data; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, + CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER, + object, BUILD_END); + if (!container) + { + goto end; + } + switch (container->get_type(container)) + { + case CONTAINER_PKCS7_DATA: + case CONTAINER_PKCS7_ENCRYPTED_DATA: + case CONTAINER_PKCS7_ENVELOPED_DATA: + if (container->get_data(container, &data)) + { + break; + } + /* fall-through */ + default: + container->destroy(container); + goto end; + } + container->destroy(container); + + if (!parse_safe_contents(this, parser->get_level(parser)+1, + data)) + { + chunk_free(&data); + goto end; + } + chunk_free(&data); + break; + } + } + } + success = parser->success(parser); +end: + parser->destroy(parser); + return success; +} + +/** + * Verify the given MAC with available passwords. + */ +static bool verify_mac(hash_algorithm_t hash, chunk_t salt, + u_int64_t iterations, chunk_t data, chunk_t mac) +{ + integrity_algorithm_t integ; + enumerator_t *enumerator; + shared_key_t *shared; + signer_t *signer; + chunk_t key, calculated; + bool success = FALSE; + + integ = hasher_algorithm_to_integrity(hash, mac.len); + signer = lib->crypto->create_signer(lib->crypto, integ); + if (!signer) + { + return FALSE; + } + key = chunk_alloca(signer->get_key_size(signer)); + calculated = chunk_alloca(signer->get_block_size(signer)); + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + if (!pkcs12_derive_key(hash, shared->get_key(shared), salt, iterations, + PKCS12_KEY_MAC, key)) + { + break; + } + if (!signer->set_key(signer, key) || + !signer->get_signature(signer, data, calculated.ptr)) + { + break; + } + if (chunk_equals(mac, calculated)) + { + success = TRUE; + break; + } + } + enumerator->destroy(enumerator); + signer->destroy(signer); + return success; +} + +/** + * ASN.1 definition of digestInfo + */ +static const asn1Object_t digestInfoObjects[] = { + { 0, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "digest", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define DIGEST_INFO_ALGORITHM 1 +#define DIGEST_INFO_DIGEST 2 + +/** + * Parse a digestInfo structure + */ +static bool parse_digest_info(chunk_t blob, int level0, hash_algorithm_t *hash, + chunk_t *digest) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + bool success; + + parser = asn1_parser_create(digestInfoObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + + { + case DIGEST_INFO_ALGORITHM: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser)+1, NULL); + + *hash = hasher_algorithm_from_oid(oid); + break; + } + case DIGEST_INFO_DIGEST: + { + *digest = object; + break; + } + default: + break; + } + } + success = parser->success(parser); + parser->destroy(parser); + return success; +} + +/** + * ASN.1 definition of a PFX structure + */ +static const asn1Object_t PFXObjects[] = { + { 0, "PFX", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "authSafe", ASN1_SEQUENCE, ASN1_OBJ }, /* 2 */ + { 1, "macData", ASN1_SEQUENCE, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 2, "mac", ASN1_SEQUENCE, ASN1_RAW }, /* 4 */ + { 2, "macSalt", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ + { 2, "iterations", ASN1_INTEGER, ASN1_DEF|ASN1_BODY }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PFX_AUTH_SAFE 2 +#define PFX_MAC 4 +#define PFX_SALT 5 +#define PFX_ITERATIONS 6 + +/** + * Parse an ASN.1 encoded PFX structure + */ +static bool parse_PFX(private_pkcs12_t *this, chunk_t blob) +{ + asn1_parser_t *parser; + int objectID; + chunk_t object, auth_safe, digest = chunk_empty, salt = chunk_empty, + data = chunk_empty; + hash_algorithm_t hash = HASH_UNKNOWN; + container_t *container = NULL; + u_int64_t iterations = 0; + bool success = FALSE; + + parser = asn1_parser_create(PFXObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PFX_AUTH_SAFE: + { + auth_safe = object; + break; + } + case PFX_MAC: + { + if (!parse_digest_info(object, parser->get_level(parser)+1, + &hash, &digest)) + { + goto end_parse; + } + break; + } + case PFX_SALT: + { + salt = object; + break; + } + case PFX_ITERATIONS: + { + iterations = object.len ? asn1_parse_integer_uint64(object) : 1; + break; + } + } + } + success = parser->success(parser); + +end_parse: + parser->destroy(parser); + if (!success) + { + return FALSE; + } + + success = FALSE; + DBG2(DBG_ASN, "-- > --"); + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, auth_safe, BUILD_END); + if (container && container->get_data(container, &data)) + { + if (hash != HASH_UNKNOWN) + { + if (container->get_type(container) != CONTAINER_PKCS7_DATA) + { + goto end; + } + if (!verify_mac(hash, salt, iterations, data, digest)) + { + DBG1(DBG_ASN, " MAC verification of PKCS#12 container failed"); + goto end; + } + } + else + { + enumerator_t *enumerator; + auth_cfg_t *auth; + + if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA) + { + goto end; + } + enumerator = container->create_signature_enumerator(container); + if (!enumerator->enumerate(enumerator, &auth)) + { + DBG1(DBG_ASN, " signature verification of PKCS#12 container " + "failed"); + enumerator->destroy(enumerator); + goto end; + } + enumerator->destroy(enumerator); + } + success = parse_authenticated_safe(this, data); + } +end: + DBG2(DBG_ASN, "-- < --"); + DESTROY_IF(container); + chunk_free(&data); + return success; +} + +/** + * See header. + */ +pkcs12_t *pkcs12_decode(container_type_t type, va_list args) +{ + private_pkcs12_t *this; + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (blob.len) + { + if (blob.len >= 2 && + blob.ptr[0] == ASN1_SEQUENCE && blob.ptr[1] == 0x80) + { /* looks like infinite length BER encoding, but we can't handle it. + */ + return NULL; + } + this = pkcs12_create(); + if (parse_PFX(this, blob)) + { + return &this->public; + } + destroy(this); + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h new file mode 100644 index 000000000..e2998968f --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup pkcs12_decode pkcs12_decode + * @{ @ingroup pkcs12 + */ + +#ifndef PKCS12_DECODE_H_ +#define PKCS12_DECODE_H_ + +#include +#include + +/** + * Load a PKCS#12 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS12 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs12_t *pkcs12_decode(container_type_t type, va_list args); + +#endif /** PKCS12_DECODE_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c new file mode 100644 index 000000000..ae0fb9093 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "pkcs12_plugin.h" + +#include + +#include "pkcs12_decode.h" + +typedef struct private_pkcs12_plugin_t private_pkcs12_plugin_t; + +/** + * private data of pkcs12_plugin + */ +struct private_pkcs12_plugin_t { + + /** + * public functions + */ + pkcs12_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_pkcs12_plugin_t *this) +{ + return "pkcs12"; +} + +METHOD(plugin_t, get_features, int, + private_pkcs12_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(CONTAINER_DECODE, pkcs12_decode, FALSE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_pkcs12_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *pkcs12_plugin_create() +{ + private_pkcs12_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} + diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h new file mode 100644 index 000000000..3bd7f2df3 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 PURPSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs12 pkcs12 + * @ingroup plugins + * + * @defgroup pkcs12_plugin pkcs12_plugin + * @{ @ingroup pkcs12 + */ + +#ifndef PKCS12_PLUGIN_H_ +#define PKCS12_PLUGIN_H_ + +#include + +typedef struct pkcs12_plugin_t pkcs12_plugin_t; + +/** + * Plugin providing PKCS#12 decoding functions + */ +struct pkcs12_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** PKCS12_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/Makefile.am b/src/libstrongswan/plugins/pkcs7/Makefile.am index 6310daece..000bde23e 100644 --- a/src/libstrongswan/plugins/pkcs7/Makefile.am +++ b/src/libstrongswan/plugins/pkcs7/Makefile.am @@ -12,6 +12,7 @@ endif libstrongswan_pkcs7_la_SOURCES = \ pkcs7_generic.h pkcs7_generic.c \ pkcs7_signed_data.h pkcs7_signed_data.c \ + pkcs7_encrypted_data.h pkcs7_encrypted_data.c \ pkcs7_enveloped_data.h pkcs7_enveloped_data.c \ pkcs7_data.h pkcs7_data.c \ pkcs7_attributes.h pkcs7_attributes.c \ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c new file mode 100644 index 000000000..2c414c391 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "pkcs7_encrypted_data.h" + +#include +#include +#include +#include +#include + +typedef struct private_pkcs7_encrypted_data_t private_pkcs7_encrypted_data_t; + +/** + * Private data of a PKCS#7 signed-data container. + */ +struct private_pkcs7_encrypted_data_t { + + /** + * Implements pkcs7_t. + */ + pkcs7_t public; + + /** + * Decrypted content + */ + chunk_t content; + + /** + * Encrypted and encoded PKCS#7 encrypted-data + */ + chunk_t encoding; +}; + +/** + * Decrypt encrypted-data with available passwords + */ +static bool decrypt(pkcs5_t *pkcs5, chunk_t data, chunk_t *decrypted) +{ + enumerator_t *enumerator; + shared_key_t *shared; + bool success = FALSE; + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + if (pkcs5->decrypt(pkcs5, shared->get_key(shared), data, decrypted)) + { + success = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return success; +} + +/** + * ASN.1 definition of the PKCS#7 encrypted-data type + */ +static const asn1Object_t encryptedDataObjects[] = { + { 0, "encryptedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 2 */ + { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 3 */ + { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 4 */ + { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKCS7_VERSION 1 +#define PKCS7_CONTENT_TYPE 3 +#define PKCS7_CONTENT_ENC_ALGORITHM 4 +#define PKCS7_ENCRYPTED_CONTENT 5 + +/** + * Parse and decrypt encrypted-data + */ +static bool parse(private_pkcs7_encrypted_data_t *this, chunk_t content) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID, version; + bool success = FALSE; + chunk_t encrypted = chunk_empty; + pkcs5_t *pkcs5 = NULL; + + parser = asn1_parser_create(encryptedDataObjects, content); + + while (parser->iterate(parser, &objectID, &object)) + { + int level = parser->get_level(parser); + + switch (objectID) + { + case PKCS7_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + if (version != 0) + { + DBG1(DBG_LIB, "encryptedData version is not 0"); + goto end; + } + break; + case PKCS7_CONTENT_TYPE: + if (asn1_known_oid(object) != OID_PKCS7_DATA) + { + DBG1(DBG_LIB, "encrypted content not of type pkcs7 data"); + goto end; + } + break; + case PKCS7_CONTENT_ENC_ALGORITHM: + pkcs5 = pkcs5_from_algorithmIdentifier(object, level + 1); + if (!pkcs5) + { + DBG1(DBG_LIB, "failed to detect PKCS#5 scheme"); + goto end; + } + break; + case PKCS7_ENCRYPTED_CONTENT: + encrypted = object; + break; + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + success = success && decrypt(pkcs5, encrypted, &this->content); + DESTROY_IF(pkcs5); + return success; +} + +METHOD(container_t, get_type, container_type_t, + private_pkcs7_encrypted_data_t *this) +{ + return CONTAINER_PKCS7_ENCRYPTED_DATA; +} + +METHOD(container_t, get_data, bool, + private_pkcs7_encrypted_data_t *this, chunk_t *data) +{ + if (this->content.len) + { + *data = chunk_clone(this->content); + return TRUE; + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs7_encrypted_data_t *this, chunk_t *data) +{ + *data = chunk_clone(this->encoding); + return TRUE; +} + +METHOD(container_t, destroy, void, + private_pkcs7_encrypted_data_t *this) +{ + free(this->content.ptr); + free(this->encoding.ptr); + free(this); +} + +/** + * Generic constructor + */ +static private_pkcs7_encrypted_data_t* create_empty() +{ + private_pkcs7_encrypted_data_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = (void*)enumerator_create_empty, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .create_cert_enumerator = (void*)enumerator_create_empty, + .get_attribute = (void*)return_false, + }, + ); + + return this; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_encrypted_data_load(chunk_t encoding, chunk_t content) +{ + private_pkcs7_encrypted_data_t *this = create_empty(); + + this->encoding = chunk_clone(encoding); + if (!parse(this, content)) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h new file mode 100644 index 000000000..b685557fc --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_encrypted_data.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup pkcs7_encrypted_data pkcs7_encrypted_data + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_ENCRYPTED_DATA_H_ +#define PKCS7_ENCRYPTED_DATA_H_ + +#include +#include + +/** + * Parse a PKCS#7 encrypted-data container. + * + * @param encoding full contentInfo encoding + * @param content DER encoded content from contentInfo + * @return CONTAINER_PKCS7_ENCRYPTED_DATA container, NULL on failure + */ +pkcs7_t *pkcs7_encrypted_data_load(chunk_t encoding, chunk_t content); + +#endif /** PKCS7_ENCRYPTED_DATA_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c index 35d8d11a7..24d7cd848 100644 --- a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c @@ -20,6 +20,7 @@ #include "pkcs7_generic.h" #include "pkcs7_data.h" #include "pkcs7_signed_data.h" +#include "pkcs7_encrypted_data.h" #include "pkcs7_enveloped_data.h" #include @@ -85,6 +86,8 @@ end: return pkcs7_signed_data_load(blob, content); case OID_PKCS7_ENVELOPED_DATA: return pkcs7_enveloped_data_load(blob, content); + case OID_PKCS7_ENCRYPTED_DATA: + return pkcs7_encrypted_data_load(blob, content); default: DBG1(DBG_ASN, "pkcs7 content type %d not supported", type); return NULL; diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c index 26a3620d7..e93a8361c 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /** @@ -100,450 +101,39 @@ end: return key; } -/** - * Verify padding of decrypted blob. - * Length of blob is adjusted accordingly. - */ -static bool verify_padding(chunk_t *blob) -{ - u_int8_t padding, count; - - padding = count = blob->ptr[blob->len - 1]; - if (padding > 8) - { - return FALSE; - } - for (; blob->len && count; --blob->len, --count) - { - if (blob->ptr[blob->len - 1] != padding) - { - return FALSE; - } - } - return TRUE; -} - -/** - * Prototype for key derivation functions. - */ -typedef bool (*kdf_t)(void *generator, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key); - /** * Try to decrypt the given blob with multiple passwords using the given - * key derivation function. keymat is where the kdf function writes the key - * to, key and iv point to the actual keys and initialization vectors resp. + * pkcs5 object. */ -static private_key_t *decrypt_private_key(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, kdf_t kdf, - void *generator, chunk_t salt, u_int64_t iterations, - chunk_t keymat, chunk_t key, chunk_t iv) +static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob) { enumerator_t *enumerator; shared_key_t *shared; - crypter_t *crypter; private_key_t *private_key = NULL; - crypter = lib->crypto->create_crypter(lib->crypto, encr, key_len); - if (!crypter) - { - DBG1(DBG_ASN, " %N encryption algorithm not available", - encryption_algorithm_names, encr); - return NULL; - } - if (blob.len % crypter->get_block_size(crypter)) - { - DBG1(DBG_ASN, " data size is not a multiple of block size"); - crypter->destroy(crypter); - return NULL; - } - enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { chunk_t decrypted; - if (!kdf(generator, shared->get_key(shared), salt, iterations, keymat)) + if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted)) { continue; } - if (!crypter->set_key(crypter, key) || - !crypter->decrypt(crypter, blob, iv, &decrypted)) + private_key = parse_private_key(decrypted); + if (private_key) { - continue; - } - if (verify_padding(&decrypted)) - { - private_key = parse_private_key(decrypted); - if (private_key) - { - chunk_clear(&decrypted); - break; - } + chunk_clear(&decrypted); + break; } chunk_free(&decrypted); } enumerator->destroy(enumerator); - crypter->destroy(crypter); return private_key; } -/** - * Function F of PBKDF2 - */ -static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed, - u_int64_t iterations) -{ - chunk_t u; - u_int64_t i; - - u = chunk_alloca(prf->get_block_size(prf)); - if (!prf->get_bytes(prf, seed, u.ptr)) - { - return FALSE; - } - memcpy(block.ptr, u.ptr, block.len); - - for (i = 1; i < iterations; i++) - { - if (!prf->get_bytes(prf, u, u.ptr)) - { - return FALSE; - } - memxor(block.ptr, u.ptr, block.len); - } - return TRUE; -} - -/** - * PBKDF2 key derivation function - */ -static bool pbkdf2(prf_t *prf, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key) -{ - chunk_t keymat, block, seed; - size_t blocks; - u_int32_t i = 0, *ni; - - if (!prf->set_key(prf, password)) - { - return FALSE; - } - - block.len = prf->get_block_size(prf); - blocks = (key.len - 1) / block.len + 1; - keymat = chunk_alloca(blocks * block.len); - - seed = chunk_cata("cc", salt, chunk_from_thing(i)); - ni = (u_int32_t*)(seed.ptr + salt.len); - - for (; i < blocks; i++) - { - *ni = htonl(i + 1); - block.ptr = keymat.ptr + (i * block.len); - if (!pbkdf2_f(block, prf, seed, iterations)) - { - return FALSE; - } - } - - memcpy(key.ptr, keymat.ptr, key.len); - - return TRUE; -} - -/** - * Decrypt an encrypted PKCS#8 encoded private key according to PBES2 - */ -static private_key_t *decrypt_private_key_pbes2(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, - chunk_t iv, pseudo_random_function_t prf_func, - chunk_t salt, u_int64_t iterations) -{ - private_key_t *private_key; - prf_t *prf; - chunk_t key; - - prf = lib->crypto->create_prf(lib->crypto, prf_func); - if (!prf) - { - DBG1(DBG_ASN, " %N prf algorithm not available", - pseudo_random_function_names, prf_func); - return NULL; - } - - key = chunk_alloca(key_len); - - private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf2, prf, - salt, iterations, key, key, iv); - - prf->destroy(prf); - return private_key; -} - -/** - * PBKDF1 key derivation function - */ -static bool pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt, - u_int64_t iterations, chunk_t key) -{ - chunk_t hash; - u_int64_t i; - - hash = chunk_alloca(hasher->get_hash_size(hasher)); - if (!hasher->get_hash(hasher, password, NULL) || - !hasher->get_hash(hasher, salt, hash.ptr)) - { - return FALSE; - } - - for (i = 1; i < iterations; i++) - { - if (!hasher->get_hash(hasher, hash, hash.ptr)) - { - return FALSE; - } - } - - memcpy(key.ptr, hash.ptr, key.len); - - return TRUE; -} - -/** - * Decrypt an encrypted PKCS#8 encoded private key according to PBES1 - */ -static private_key_t *decrypt_private_key_pbes1(chunk_t blob, - encryption_algorithm_t encr, size_t key_len, - hash_algorithm_t hash, chunk_t salt, - u_int64_t iterations) -{ - private_key_t *private_key = NULL; - hasher_t *hasher = NULL; - chunk_t keymat, key, iv; - - hasher = lib->crypto->create_hasher(lib->crypto, hash); - if (!hasher) - { - DBG1(DBG_ASN, " %N hash algorithm not available", - hash_algorithm_names, hash); - goto end; - } - if (hasher->get_hash_size(hasher) < key_len) - { - goto end; - } - - keymat = chunk_alloca(key_len * 2); - key.len = key_len; - key.ptr = keymat.ptr; - iv.len = key_len; - iv.ptr = keymat.ptr + key_len; - - private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf1, - hasher, salt, iterations, keymat, - key, iv); - -end: - DESTROY_IF(hasher); - return private_key; -} - -/** - * Parse an ASN1_INTEGER to a u_int64_t. - */ -static u_int64_t parse_asn1_integer_uint64(chunk_t blob) -{ - u_int64_t val = 0; - int i; - - for (i = 0; i < blob.len; i++) - { /* if it is longer than 8 bytes, we just use the 8 LSBs */ - val <<= 8; - val |= (u_int64_t)blob.ptr[i]; - } - return val; -} - -/** - * ASN.1 definition of a PBKDF2-params structure - * The salt is actually a CHOICE and could be an AlgorithmIdentifier from - * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions). - */ -static const asn1Object_t pbkdf2ParamsObjects[] = { - { 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ - { 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */ - { 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ - { 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBKDF2_SALT 1 -#define PBKDF2_ITERATION_COUNT 2 -#define PBKDF2_KEY_LENGTH 3 -#define PBKDF2_PRF 5 - -/** - * Parse a PBKDF2-params structure - */ -static void parse_pbkdf2_params(chunk_t blob, chunk_t *salt, - u_int64_t *iterations, size_t *key_len, - pseudo_random_function_t *prf) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - - parser = asn1_parser_create(pbkdf2ParamsObjects, blob); - - *key_len = 0; /* key_len is optional */ - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBKDF2_SALT: - { - *salt = object; - break; - } - case PBKDF2_ITERATION_COUNT: - { - *iterations = parse_asn1_integer_uint64(object); - break; - } - case PBKDF2_KEY_LENGTH: - { - *key_len = (size_t)parse_asn1_integer_uint64(object); - break; - } - case PBKDF2_PRF: - { /* defaults to id-hmacWithSHA1 */ - *prf = PRF_HMAC_SHA1; - break; - } - } - } - - parser->destroy(parser); -} - -/** - * ASN.1 definition of a PBES2-params structure - */ -static const asn1Object_t pbes2ParamsObjects[] = { - { 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */ - { 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBES2PARAMS_KEY_DERIVATION_FUNC 1 -#define PBES2PARAMS_ENCRYPTION_SCHEME 2 - -/** - * Parse a PBES2-params structure - */ -static void parse_pbes2_params(chunk_t blob, chunk_t *salt, - u_int64_t *iterations, size_t *key_len, - pseudo_random_function_t *prf, - encryption_algorithm_t *encr, chunk_t *iv) -{ - asn1_parser_t *parser; - chunk_t object, params; - int objectID; - - parser = asn1_parser_create(pbes2ParamsObjects, blob); - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBES2PARAMS_KEY_DERIVATION_FUNC: - { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - if (oid != OID_PBKDF2) - { /* unsupported key derivation function */ - goto end; - } - parse_pbkdf2_params(params, salt, iterations, key_len, prf); - break; - } - case PBES2PARAMS_ENCRYPTION_SCHEME: - { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - if (oid != OID_3DES_EDE_CBC) - { /* unsupported encryption scheme */ - goto end; - } - if (*key_len <= 0) - { /* default key len for DES-EDE3-CBC-Pad */ - *key_len = 24; - } - if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING, - parser->get_level(parser) + 1, "IV")) - { - goto end; - } - *encr = ENCR_3DES; - *iv = params; - break; - } - } - } - -end: - parser->destroy(parser); -} - -/** - * ASN.1 definition of a PBEParameter structure - */ -static const asn1Object_t pbeParameterObjects[] = { - { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ - { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PBEPARAM_SALT 1 -#define PBEPARAM_ITERATION_COUNT 2 - -/** - * Parse a PBEParameter structure - */ -static void parse_pbe_parameters(chunk_t blob, chunk_t *salt, - u_int64_t *iterations) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - - parser = asn1_parser_create(pbeParameterObjects, blob); - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case PBEPARAM_SALT: - { - *salt = object; - break; - } - case PBEPARAM_ITERATION_COUNT: - { - *iterations = parse_asn1_integer_uint64(object); - break; - } - } - } - - parser->destroy(parser); -} - /** * ASN.1 definition of an encryptedPrivateKeyInfo structure */ @@ -563,14 +153,10 @@ static const asn1Object_t encryptedPKIObjects[] = { static private_key_t *parse_encrypted_private_key(chunk_t blob) { asn1_parser_t *parser; - chunk_t object, params, salt = chunk_empty, iv = chunk_empty; - u_int64_t iterations = 0; + chunk_t object; int objectID; - encryption_algorithm_t encr = ENCR_UNDEFINED; - hash_algorithm_t hash = HASH_UNKNOWN; - pseudo_random_function_t prf = PRF_UNDEFINED; private_key_t *key = NULL; - size_t key_len = 8; + pkcs5_t *pkcs5 = NULL; parser = asn1_parser_create(encryptedPKIObjects, blob); @@ -580,49 +166,24 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob) { case EPKINFO_ENCRYPTION_ALGORITHM: { - int oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, ¶ms); - - switch (oid) + pkcs5 = pkcs5_from_algorithmIdentifier(object, + parser->get_level(parser) + 1); + if (!pkcs5) { - case OID_PBE_MD5_DES_CBC: - encr = ENCR_DES; - hash = HASH_MD5; - parse_pbe_parameters(params, &salt, &iterations); - break; - case OID_PBE_SHA1_DES_CBC: - encr = ENCR_DES; - hash = HASH_SHA1; - parse_pbe_parameters(params, &salt, &iterations); - break; - case OID_PBES2: - parse_pbes2_params(params, &salt, &iterations, - &key_len, &prf, &encr, &iv); - break; - default: - /* encryption scheme not supported */ - goto end; + goto end; } break; } case EPKINFO_ENCRYPTED_DATA: { - if (prf != PRF_UNDEFINED) - { - key = decrypt_private_key_pbes2(object, encr, key_len, iv, - prf, salt, iterations); - } - else - { - key = decrypt_private_key_pbes1(object, encr, key_len, hash, - salt, iterations); - } + key = decrypt_private_key(pkcs5, object); break; } } } end: + DESTROY_IF(pkcs5); parser->destroy(parser); return key; } diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c index f78c83054..129fbb045 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c @@ -43,6 +43,7 @@ METHOD(plugin_t, get_features, int, { static plugin_feature_t f[] = { PLUGIN_REGISTER(PRIVKEY, pkcs8_private_key_load, FALSE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), }; diff --git a/src/libstrongswan/plugins/rc2/Makefile.am b/src/libstrongswan/plugins/rc2/Makefile.am new file mode 100644 index 000000000..156574de3 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-rc2.la +else +plugin_LTLIBRARIES = libstrongswan-rc2.la +endif + +libstrongswan_rc2_la_SOURCES = \ + rc2_plugin.h rc2_plugin.c rc2_crypter.c rc2_crypter.h + +libstrongswan_rc2_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/rc2/rc2_crypter.c b/src/libstrongswan/plugins/rc2/rc2_crypter.c new file mode 100644 index 000000000..256acf817 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_crypter.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "rc2_crypter.h" + +typedef struct private_rc2_crypter_t private_rc2_crypter_t; + +#define RC2_BLOCK_SIZE 8 + +#define ROL16(x, k) ({ u_int16_t _x = (x); (_x << (k)) | (_x >> (16 - (k))); }) +#define ROR16(x, k) ({ u_int16_t _x = (x); (_x >> (k)) | (_x << (16 - (k))); }) + +#define GET16(x) ({ u_char *_x = (x); (u_int16_t)_x[0] | ((u_int16_t)_x[1] << 8); }) +#define PUT16(x, v) ({ u_char *_x = (x); u_int16_t _v = (v); _x[0] = _v, _x[1] = _v >> 8; }) + +/** + * Private data of rc2_crypter_t + */ +struct private_rc2_crypter_t { + + /** + * Public interface + */ + rc2_crypter_t public; + + /** + * The expanded key in 16-bit words + */ + u_int16_t K[64]; + + /** + * Key size in bytes + */ + size_t T; + + /** + * Effective key size in bits + */ + size_t T1; +}; + +/** + * PITABLE + */ +static const u_char PITABLE[256] = +{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, + 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, + 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, + 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, + 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, + 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, + 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, + 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, + 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, + 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, + 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, + 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, + 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, + 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, + 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, + 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +}; + +/** + * Encrypt a single block of data + */ +static void encrypt_block(private_rc2_crypter_t *this, u_char R[]) +{ + register u_int16_t R0, R1, R2, R3, *Kj; + int rounds = 3, mix = 5; + + R0 = GET16(R); + R1 = GET16(R + 2); + R2 = GET16(R + 4); + R3 = GET16(R + 6); + Kj = &this->K[0]; + + /* 5 mix, mash, 6 mix, mash, 5 mix */ + while (TRUE) + { + /* mix */ + R0 = ROL16(R0 + *(Kj++) + (R3 & R2) + (~R3 & R1), 1); + R1 = ROL16(R1 + *(Kj++) + (R0 & R3) + (~R0 & R2), 2); + R2 = ROL16(R2 + *(Kj++) + (R1 & R0) + (~R1 & R3), 3); + R3 = ROL16(R3 + *(Kj++) + (R2 & R1) + (~R2 & R0), 5); + + if (--mix == 0) + { + if (--rounds == 0) + { + break; + } + mix = (rounds == 2) ? 6 : 5; + /* mash */ + R0 += this->K[R3 & 63]; + R1 += this->K[R0 & 63]; + R2 += this->K[R1 & 63]; + R3 += this->K[R2 & 63]; + } + } + + PUT16(R, R0); + PUT16(R + 2, R1); + PUT16(R + 4, R2); + PUT16(R + 6, R3); +} + +/** + * Decrypt a single block of data. + */ +static void decrypt_block(private_rc2_crypter_t *this, u_char R[]) +{ + register u_int16_t R0, R1, R2, R3, *Kj; + int rounds = 3, mix = 5; + + R0 = GET16(R); + R1 = GET16(R + 2); + R2 = GET16(R + 4); + R3 = GET16(R + 6); + Kj = &this->K[63]; + + /* 5 r-mix, r-mash, 6 r-mix, r-mash, 5 r-mix */ + while (TRUE) + { + /* r-mix */ + R3 = ROR16(R3, 5); + R3 = R3 - *(Kj--) - (R2 & R1) - (~R2 & R0); + R2 = ROR16(R2, 3); + R2 = R2 - *(Kj--) - (R1 & R0) - (~R1 & R3); + R1 = ROR16(R1, 2); + R1 = R1 - *(Kj--) - (R0 & R3) - (~R0 & R2); + R0 = ROR16(R0, 1); + R0 = R0 - *(Kj--) - (R3 & R2) - (~R3 & R1); + + if (--mix == 0) + { + if (--rounds == 0) + { + break; + } + mix = (rounds == 2) ? 6 : 5; + /* r-mash */ + R3 -= this->K[R2 & 63]; + R2 -= this->K[R1 & 63]; + R1 -= this->K[R0 & 63]; + R0 -= this->K[R3 & 63]; + } + } + + PUT16(R, R0); + PUT16(R + 2, R1); + PUT16(R + 4, R2); + PUT16(R + 6, R3); +} + +METHOD(crypter_t, decrypt, bool, + private_rc2_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + u_int8_t *in, *out, *prev; + + if (data.len % RC2_BLOCK_SIZE || iv.len != RC2_BLOCK_SIZE) + { + return FALSE; + } + + in = data.ptr + data.len - RC2_BLOCK_SIZE; + out = data.ptr; + if (decrypted) + { + *decrypted = chunk_alloc(data.len); + out = decrypted->ptr; + } + out += data.len - RC2_BLOCK_SIZE; + + prev = in; + for (; in >= data.ptr; in -= RC2_BLOCK_SIZE, out -= RC2_BLOCK_SIZE) + { + if (decrypted) + { + memcpy(out, in, RC2_BLOCK_SIZE); + } + decrypt_block(this, out); + prev -= RC2_BLOCK_SIZE; + if (prev < data.ptr) + { + prev = iv.ptr; + } + memxor(out, prev, RC2_BLOCK_SIZE); + } + return TRUE; +} + +METHOD(crypter_t, encrypt, bool, + private_rc2_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + u_int8_t *in, *out, *end, *prev; + + if (data.len % RC2_BLOCK_SIZE || iv.len != RC2_BLOCK_SIZE) + { + return FALSE; + } + + in = data.ptr; + end = data.ptr + data.len; + out = data.ptr; + if (encrypted) + { + *encrypted = chunk_alloc(data.len); + out = encrypted->ptr; + } + + prev = iv.ptr; + for (; in < end; in += RC2_BLOCK_SIZE, out += RC2_BLOCK_SIZE) + { + if (encrypted) + { + memcpy(out, in, RC2_BLOCK_SIZE); + } + memxor(out, prev, RC2_BLOCK_SIZE); + encrypt_block(this, out); + prev = out; + } + return TRUE; +} + +METHOD(crypter_t, get_block_size, size_t, + private_rc2_crypter_t *this) +{ + return RC2_BLOCK_SIZE; +} + +METHOD(crypter_t, get_iv_size, size_t, + private_rc2_crypter_t *this) +{ + return RC2_BLOCK_SIZE; +} + +METHOD(crypter_t, get_key_size, size_t, + private_rc2_crypter_t *this) +{ + return this->T; +} + +METHOD(crypter_t, set_key, bool, + private_rc2_crypter_t *this, chunk_t key) +{ + u_int8_t L[128], T8, TM, idx; + int i; + + if (key.len != this->T) + { + return FALSE; + } + for (i = 0; i < key.len; i++) + { + L[i] = key.ptr[i]; + } + for (; i < 128; i++) + { + idx = L[i-1] + L[i-key.len]; + L[i] = PITABLE[idx]; + } + T8 = (this->T1 + 7) / 8; + TM = ~(0xff << (8 - (8*T8 - this->T1))); + L[128-T8] = PITABLE[L[128-T8] & TM]; + for (i = 127-T8; i >= 0; i--) + { + idx = L[i+1] ^ L[i+T8]; + L[i] = PITABLE[idx]; + } + for (i = 0; i < 64; i++) + { + this->K[i] = GET16(&L[i << 1]); + } + memwipe(L, sizeof(L)); + return TRUE; +} + +METHOD(crypter_t, destroy, void, + private_rc2_crypter_t *this) +{ + memwipe(this->K, sizeof(this->K)); + free(this); +} + +/* + * Described in header + */ +rc2_crypter_t *rc2_crypter_create(encryption_algorithm_t algo, size_t key_size) +{ + private_rc2_crypter_t *this; + size_t effective; + + if (algo != ENCR_RC2_CBC) + { + return NULL; + } + key_size = max(1, key_size); + effective = RC2_EFFECTIVE_KEY_LEN(key_size); + key_size = min(128, RC2_KEY_LEN(key_size)); + effective = max(1, min(1024, effective ?: key_size * 8)); + + INIT(this, + .public = { + .crypter = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_iv_size = _get_iv_size, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + }, + .T = key_size, + .T1 = effective, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/rc2/rc2_crypter.h b/src/libstrongswan/plugins/rc2/rc2_crypter.h new file mode 100644 index 000000000..d478762a6 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_crypter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup rc2_crypter rc2_crypter + * @{ @ingroup rc2_p + */ + +#ifndef RC2_CRYPTER_H_ +#define RC2_CRYPTER_H_ + +typedef struct rc2_crypter_t rc2_crypter_t; + +#include + +/** + * Class implementing the RC2 block cipher as defined in RFC 2268. + */ +struct rc2_crypter_t { + + /** + * Implements crypter_t interface. + */ + crypter_t crypter; +}; + +/** + * Constructor to create rc2_crypter_t objects. + * + * @param algo algorithm to implement (ENCR_RC2_CBC) + * @param key_size use the RC2_KEY_SIZE macro if the effective key size + * in bits is different than the actual length of the key + * @return rc2_crypter_t object, NULL if not supported + */ +rc2_crypter_t *rc2_crypter_create(encryption_algorithm_t algo, + size_t key_size); + +#endif /** RC2_CRYPTER_H_ @}*/ diff --git a/src/libstrongswan/plugins/rc2/rc2_plugin.c b/src/libstrongswan/plugins/rc2/rc2_plugin.c new file mode 100644 index 000000000..6c6fa76d6 --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_plugin.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 "rc2_plugin.h" + +#include +#include "rc2_crypter.h" + +typedef struct private_rc2_plugin_t private_rc2_plugin_t; + +/** + * Private data of rc2_plugin + */ +struct private_rc2_plugin_t { + + /** + * Public interface + */ + rc2_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_rc2_plugin_t *this) +{ + return "rc2"; +} + +METHOD(plugin_t, get_features, int, + private_rc2_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(CRYPTER, rc2_crypter_create), + PLUGIN_PROVIDE(CRYPTER, ENCR_RC2_CBC, 0), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_rc2_plugin_t *this) +{ + free(this); +} + +/* + * Described in header + */ +plugin_t *rc2_plugin_create() +{ + private_rc2_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} + diff --git a/src/libstrongswan/plugins/rc2/rc2_plugin.h b/src/libstrongswan/plugins/rc2/rc2_plugin.h new file mode 100644 index 000000000..cbbac51af --- /dev/null +++ b/src/libstrongswan/plugins/rc2/rc2_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup rc2_p rc2 + * @ingroup plugins + * + * @defgroup rc2_plugin rc2_plugin + * @{ @ingroup rc2_p + */ + +#ifndef RC2_PLUGIN_H_ +#define RC2_PLUGIN_H_ + +#include + +typedef struct rc2_plugin_t rc2_plugin_t; + +/** + * Plugin implementing RC2 (RFC 2268). + */ +struct rc2_plugin_t { + + /** + * Implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** RC2_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/test_vectors/Makefile.am b/src/libstrongswan/plugins/test_vectors/Makefile.am index 5280300a8..20716d3a0 100644 --- a/src/libstrongswan/plugins/test_vectors/Makefile.am +++ b/src/libstrongswan/plugins/test_vectors/Makefile.am @@ -26,6 +26,7 @@ libstrongswan_test_vectors_la_SOURCES = \ test_vectors/des.c \ test_vectors/idea.c \ test_vectors/null.c \ + test_vectors/rc2.c \ test_vectors/rc5.c \ test_vectors/serpent_cbc.c \ test_vectors/twofish_cbc.c \ diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors.h b/src/libstrongswan/plugins/test_vectors/test_vectors.h index a00469779..788baae57 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors.h +++ b/src/libstrongswan/plugins/test_vectors/test_vectors.h @@ -55,6 +55,14 @@ TEST_VECTOR_CRYPTER(des3_cbc2) TEST_VECTOR_CRYPTER(idea1) TEST_VECTOR_CRYPTER(idea2) TEST_VECTOR_CRYPTER(null1) +TEST_VECTOR_CRYPTER(rc2_1) +TEST_VECTOR_CRYPTER(rc2_2) +TEST_VECTOR_CRYPTER(rc2_3) +TEST_VECTOR_CRYPTER(rc2_4) +TEST_VECTOR_CRYPTER(rc2_5) +TEST_VECTOR_CRYPTER(rc2_6) +TEST_VECTOR_CRYPTER(rc2_7) +TEST_VECTOR_CRYPTER(rc2_8) TEST_VECTOR_CRYPTER(rc5_1) TEST_VECTOR_CRYPTER(rc5_2) TEST_VECTOR_CRYPTER(serpent_cbc1) diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c b/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c new file mode 100644 index 000000000..b03d12038 --- /dev/null +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/rc2.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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; either version 2 of the Licenseor (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be usefulbut + * 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 + +/** + * Test vectors from RFC 2268 + */ + +/** + * RC2 key length 8 bytes, effective key length 63 bits + */ +crypter_test_vector_t rc2_1 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 63), .len = 8, + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\xeb\xb7\x73\xf9\x93\x27\x8e\xff", +}; + +/** + * RC2 key length 8 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_2 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 64), .len = 8, + .key = "\xff\xff\xff\xff\xff\xff\xff\xff", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\xff\xff\xff\xff\xff\xff\xff\xff", + .cipher = "\x27\x8b\x27\xe4\x2e\x2f\x0d\x49", +}; + +/** + * RC2 key length 8 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_3 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(8, 64), .len = 8, + .key = "\x30\x00\x00\x00\x00\x00\x00\x00", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x10\x00\x00\x00\x00\x00\x00\x01", + .cipher = "\x30\x64\x9e\xdf\x9b\xe7\xd2\xc2", +}; + +/** + * RC2 key length 1 byte, effective key length 64 bits + */ +crypter_test_vector_t rc2_4 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(1, 64), .len = 8, + .key = "\x88", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x61\xa8\xa2\x44\xad\xac\xcc\xf0", +}; + +/** + * RC2 key length 7 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_5 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(7, 64), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x6c\xcf\x43\x08\x97\x4c\x26\x7f", +}; + +/** + * RC2 key length 16 bytes, effective key length 64 bits + */ +crypter_test_vector_t rc2_6 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(16, 64), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x1a\x80\x7d\x27\x2b\xbe\x5d\xb1", +}; + +/** + * RC2 key length 16 bytes, effective key length 128 bits + */ +crypter_test_vector_t rc2_7 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(16, 128), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x22\x69\x55\x2a\xb0\xf8\x5c\xa6", +}; + +/** + * RC2 key length 33 bytes, effective key length 129 bits + */ +crypter_test_vector_t rc2_8 = { + .alg = ENCR_RC2_CBC, .key_size = RC2_KEY_SIZE(33, 129), .len = 8, + .key = "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2" + "\x16\xf8\x0a\x6f\x85\x92\x05\x84\xc4\x2f\xce\xb0\xbe\x25\x5d\xaf\x1e", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .plain = "\x00\x00\x00\x00\x00\x00\x00\x00", + .cipher = "\x5b\x78\xd3\xa4\x3d\xff\xf1\xf1", +}; diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 26e3e43fc..4f3c9f78b 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -475,13 +475,6 @@ char *whitelist[] = { "Curl_client_write", /* ClearSilver */ "nerr_init", - /* OpenSSL */ - "RSA_new_method", - "DH_new_method", - "ENGINE_load_builtin_engines", - "OPENSSL_config", - "ecdsa_check", - "ERR_put_error", /* libgcrypt */ "gcry_control", "gcry_check_version",