Merge branch 'charon-cmd-pkcs12'
Adds support for PKCS#12 files in charon-cmd and ipsec.secrets. Also fixes the cleanup of the OpenSSL library in the openssl plugin.
This commit is contained in:
+9
-1
@@ -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
|
||||
|
||||
+17
-4
@@ -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 [ <selectors> ] : RSA <private key file> [ <passphrase> | %prompt ]
|
||||
.B : RSA <private key file> [ <passphrase> | %prompt ]
|
||||
.TQ
|
||||
.B [ <selectors> ] : ECDSA <private key file> [ <passphrase> | %prompt ]
|
||||
.B : ECDSA <private key file> [ <passphrase> | %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 <PKCS#12 file> [ <passphrase> | %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 <user id> : EAP <secret>
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
#include <credentials/containers/pkcs12.h>
|
||||
#include <credentials/sets/callback_cred.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 <credentials/certificates/x509.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
#include <credentials/certificates/ac.h>
|
||||
#include <credentials/containers/pkcs12.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
#include <credentials/sets/callback_cred.h>
|
||||
#include <collections/linked_list.h>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <utils/debug.h>
|
||||
#include <credentials/certificates/x509.h>
|
||||
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <utils/debug.h>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <credentials/containers/container.h>
|
||||
#include <crypto/hashers/hasher.h>
|
||||
|
||||
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_ @}*/
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <utils/debug.h>
|
||||
#include <asn1/oid.h>
|
||||
#include <asn1/asn1.h>
|
||||
#include <asn1/asn1_parser.h>
|
||||
#include <credentials/containers/pkcs12.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <utils/chunk.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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, },
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
|
||||
#include "openssl_pkcs12.h"
|
||||
#include "openssl_util.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <credentials/containers/pkcs12.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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 <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/rand.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <utils/debug.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/thread_value.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Tobias Brunner
|
||||
* Copyright (C) 2009 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <credentials/credential_factory.h>
|
||||
#include <credentials/keys/private_key.h>
|
||||
#include <credentials/certificates/certificate.h>
|
||||
#include <credentials/containers/container.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <utils/debug.h>
|
||||
#include <asn1/oid.h>
|
||||
#include <asn1/asn1.h>
|
||||
#include <asn1/asn1_parser.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <credentials/builder.h>
|
||||
#include <credentials/containers/pkcs12.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <library.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <plugins/plugin.h>
|
||||
|
||||
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_ @}*/
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <asn1/asn1.h>
|
||||
#include <asn1/asn1_parser.h>
|
||||
#include <asn1/oid.h>
|
||||
#include <crypto/pkcs5.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <credentials/builder.h>
|
||||
#include <credentials/containers/pkcs7.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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 <utils/debug.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <asn1/oid.h>
|
||||
#include <asn1/asn1.h>
|
||||
#include <asn1/asn1_parser.h>
|
||||
#include <crypto/pkcs5.h>
|
||||
#include <credentials/keys/private_key.h>
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <crypto/crypters/crypter.h>
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <library.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <plugins/plugin.h>
|
||||
|
||||
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_ @}*/
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <crypto/crypto_tester.h>
|
||||
|
||||
/**
|
||||
* 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",
|
||||
};
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user