/* The main SILC PKCS structure. */
struct SilcPKCSStruct {
- void *context;
- SilcPKCSObject *pkcs;
- SilcUInt32 key_len;
+ void *context; /* Algorithm internal context */
+ SilcPKCSObject *pkcs; /* Algorithm implementation */
+ SilcUInt32 key_len; /* Key length in bits */
};
#ifndef SILC_EPOC
SILC_LOG_DEBUG(("Registering new PKCS `%s'", pkcs->name));
+ /* Check if exists already */
+ if (silc_pkcs_list) {
+ SilcPKCSObject *entry;
+ silc_dlist_start(silc_pkcs_list);
+ while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+ if (!strcmp(entry->name, pkcs->name))
+ return FALSE;
+ }
+ }
+
new = silc_calloc(1, sizeof(*new));
new->name = strdup(pkcs->name);
new->init = pkcs->init;
/* Return TRUE if PKCS algorithm `name' is supported. */
-int silc_pkcs_is_supported(const unsigned char *name)
+bool silc_pkcs_is_supported(const unsigned char *name)
{
#ifndef SILC_EPOC
SilcPKCSObject *entry;
/* Generate new key pair into the `pkcs' context. */
-int silc_pkcs_generate_key(SilcPKCS pkcs, SilcUInt32 bits_key_len,
- SilcRng rng)
+bool silc_pkcs_generate_key(SilcPKCS pkcs, SilcUInt32 bits_key_len,
+ SilcRng rng)
{
return pkcs->pkcs->init(pkcs->context, bits_key_len, rng);
}
/* Sets private key from SilcPrivateKey. */
-int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key)
+SilcUInt32 silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key)
{
- return pkcs->pkcs->set_private_key(pkcs->context, private_key->prv,
- private_key->prv_len);
+ SilcUInt32 key_len;
+ key_len = pkcs->pkcs->set_private_key(pkcs->context, private_key->prv,
+ private_key->prv_len);
+ if (!pkcs->key_len)
+ pkcs->key_len = key_len;
+ return pkcs->key_len;
}
/* Sets private key from data. */
-int silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv,
- SilcUInt32 prv_len)
+SilcUInt32 silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv,
+ SilcUInt32 prv_len)
{
- return pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len);
+ SilcUInt32 key_len;
+ key_len = pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len);
+ if (!pkcs->key_len)
+ pkcs->key_len = key_len;
+ return pkcs->key_len;
}
/* Encrypts */
-int silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
- unsigned char *dst, SilcUInt32 *dst_len)
+bool silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
+ unsigned char *dst, SilcUInt32 *dst_len)
{
return pkcs->pkcs->encrypt(pkcs->context, src, src_len, dst, dst_len);
}
/* Decrypts */
-int silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
- unsigned char *dst, SilcUInt32 *dst_len)
+bool silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
+ unsigned char *dst, SilcUInt32 *dst_len)
{
return pkcs->pkcs->decrypt(pkcs->context, src, src_len, dst, dst_len);
}
/* Generates signature */
-int silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
- unsigned char *dst, SilcUInt32 *dst_len)
+bool silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, SilcUInt32 src_len,
+ unsigned char *dst, SilcUInt32 *dst_len)
{
return pkcs->pkcs->sign(pkcs->context, src, src_len, dst, dst_len);
}
/* Verifies signature */
-int silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature,
- SilcUInt32 signature_len, unsigned char *data,
- SilcUInt32 data_len)
+bool silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature,
+ SilcUInt32 signature_len, unsigned char *data,
+ SilcUInt32 data_len)
{
return pkcs->pkcs->verify(pkcs->context, signature, signature_len,
data, data_len);
/* Generates signature with hash. The hash is signed. */
-int silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash,
- unsigned char *src, SilcUInt32 src_len,
- unsigned char *dst, SilcUInt32 *dst_len)
+bool silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash,
+ unsigned char *src, SilcUInt32 src_len,
+ unsigned char *dst, SilcUInt32 *dst_len)
{
unsigned char hashr[32];
SilcUInt32 hash_len;
/* Verifies signature with hash. The `data' is hashed and verified against
the `signature'. */
-int silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash,
- unsigned char *signature,
- SilcUInt32 signature_len,
- unsigned char *data,
- SilcUInt32 data_len)
+bool silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash,
+ unsigned char *signature,
+ SilcUInt32 signature_len,
+ unsigned char *data,
+ SilcUInt32 data_len)
{
unsigned char hashr[32];
SilcUInt32 hash_len;
public_key->name = strdup(name);
public_key->pk_len = pk_len;
public_key->pk = silc_calloc(pk_len, sizeof(*public_key->pk));
+ public_key->pk_type = SILC_SKE_PK_TYPE_SILC;
memcpy(public_key->pk, pk, pk_len);
if (!silc_utf8_valid(identifier, strlen(identifier))) {
}
public_key->identifier = strdup(identifier);
- public_key->len = 4 + 2 + strlen(name) + 2 + strlen(identifier) + pk_len;
+ public_key->len = 2 + strlen(name) + 2 + strlen(identifier) + pk_len;
silc_free(tmp);
return public_key;
SilcBuffer buf;
unsigned char *ret;
- buf = silc_buffer_alloc(public_key->len);
+ buf = silc_buffer_alloc(public_key->len + 4);
silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
silc_buffer_format(buf,
public_key->pk_len),
SILC_STR_END);
if (len)
- *len = public_key->len;
+ *len = public_key->len + 4;
ret = silc_calloc(buf->len, sizeof(*ret));
memcpy(ret, buf->data, buf->len);
unsigned char *ret;
SilcUInt32 totlen;
- totlen = 4 + 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len;
- buf = silc_buffer_alloc(totlen);
+ totlen = 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len;
+ buf = silc_buffer_alloc(totlen + 4);
silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
silc_buffer_format(buf,
SILC_STR_UI_XNSTRING(pk, pk_len),
SILC_STR_END);
if (len)
- *len = totlen;
+ *len = totlen + 4;
ret = silc_calloc(buf->len, sizeof(*ret));
memcpy(ret, buf->data, buf->len);
/* Decodes SILC style public key. Returns TRUE if the decoding was
successful. Allocates new public key as well. */
-int silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len,
- SilcPublicKey *public_key)
+bool silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len,
+ SilcPublicKey *public_key)
{
SilcBuffer buf;
SilcPKCS alg;
return FALSE;
}
- if (totlen != data_len) {
+#if 1 /* Backwards support, remove! */
+ if (totlen == data_len)
+ totlen -= 4;
+#endif
+
+ if (totlen + 4 != data_len) {
silc_buffer_free(buf);
return FALSE;
}
(*public_key)->identifier = ident;
(*public_key)->pk = key_data;
(*public_key)->pk_len = key_len;
+ (*public_key)->pk_type = SILC_SKE_PK_TYPE_SILC;
}
silc_buffer_free(buf);
return FALSE;
}
+/* Encodes Public Key Payload for transmitting public keys and certificates. */
+
+SilcBuffer silc_pkcs_public_key_payload_encode(SilcPublicKey public_key)
+{
+ SilcBuffer buffer;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+
+ if (!public_key)
+ return NULL;
+
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ if (!pk)
+ return NULL;
+
+ buffer = silc_buffer_alloc_size(4 + pk_len);
+ if (!buffer) {
+ silc_free(pk);
+ return NULL;
+ }
+
+ silc_buffer_format(buffer,
+ SILC_STR_UI_SHORT(pk_len),
+ SILC_STR_UI_SHORT(public_key->pk_type),
+ SILC_STR_UI_XNSTRING(pk, pk_len),
+ SILC_STR_END);
+
+ silc_free(pk);
+ return buffer;
+}
+
+/* Decode Public Key Payload and decodes the public key inside it to
+ to `payload'. */
+
+bool silc_pkcs_public_key_payload_decode(unsigned char *data,
+ SilcUInt32 data_len,
+ SilcPublicKey *public_key)
+{
+ SilcBufferStruct buf;
+ SilcUInt16 pk_len, pk_type;
+ unsigned char *pk;
+ int ret;
+
+ if (!public_key)
+ return FALSE;
+
+#if 1
+ /* XXX 1.1 version support. Check whether the data is actually raw
+ public key and attempt to decode. Remove this later! */
+ if (silc_pkcs_public_key_decode(data, data_len, public_key)) {
+ (*public_key)->pk_type = SILC_SKE_PK_TYPE_SILC;
+ return TRUE;
+ }
+#endif
+
+ silc_buffer_set(&buf, data, data_len);
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_SHORT(&pk_len),
+ SILC_STR_UI_SHORT(&pk_type),
+ SILC_STR_END);
+ if (ret < 0 || pk_len > data_len - 4)
+ return FALSE;
+
+ /* For now we support only SILC public keys */
+ if (pk_type != SILC_SKE_PK_TYPE_SILC)
+ return FALSE;
+
+ silc_buffer_pull(&buf, 4);
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_XNSTRING(&pk, pk_len),
+ SILC_STR_END);
+ silc_buffer_push(&buf, 4);
+ if (ret < 0)
+ return FALSE;
+
+ if (!silc_pkcs_public_key_decode(pk, pk_len, public_key))
+ return FALSE;
+ (*public_key)->pk_type = SILC_SKE_PK_TYPE_SILC;
+
+ return TRUE;
+}
+
/* Compares two public keys and returns TRUE if they are same key, and
FALSE if they are not same. */
strlen(public_key->identifier));
key->pk = silc_memdup(public_key->pk, public_key->pk_len);
key->pk_len = public_key->pk_len;
+ key->pk_type = public_key->pk_type;
return key;
}
ret = silc_calloc(buf->len, sizeof(*ret));
memcpy(ret, buf->data, buf->len);
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return ret;
ret = silc_calloc(buf->len, sizeof(*ret));
memcpy(ret, buf->data, buf->len);
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return ret;
}
-/* Decodes SILC style public key. Returns TRUE if the decoding was
+/* Decodes SILC style private key. Returns TRUE if the decoding was
successful. Allocates new private key as well. */
-int silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
- SilcPrivateKey *private_key)
+bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
+ SilcPrivateKey *private_key)
{
SilcBuffer buf;
SilcPKCS alg;
silc_buffer_unformat(buf,
SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
SILC_STR_END);
- if (ret == -1)
+ if (ret == -1) {
+ SILC_LOG_DEBUG(("Cannot decode private key buffer"));
goto err;
+ }
- if (pkcs_len < 1 || pkcs_len > buf->truelen)
+ if (pkcs_len < 1 || pkcs_len > buf->truelen) {
+ SILC_LOG_DEBUG(("Malformed private key buffer"));
goto err;
+ }
/* See if we support this algorithm (check only if PKCS are registered). */
if (SILC_PKCS_LIST && !silc_pkcs_is_supported(pkcs_name)) {
(check only if PKCS are registered) */
if (SILC_PKCS_LIST) {
silc_pkcs_alloc(pkcs_name, &alg);
- if (!alg->pkcs->set_private_key(alg->context, key_data, key_len))
+ if (!alg->pkcs->set_private_key(alg->context, key_data, key_len)) {
+ SILC_LOG_DEBUG(("Could not set private key data"));
goto err;
+ }
silc_pkcs_free(alg);
}
(*private_key)->prv_len = key_len;
}
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return TRUE;
silc_free(pkcs_name);
if (key_data)
silc_free(key_data);
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return FALSE;
}
/* Internal routine to save public key */
-static int silc_pkcs_save_public_key_internal(char *filename,
- unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 encoding)
+static bool silc_pkcs_save_public_key_internal(char *filename,
+ unsigned char *data,
+ SilcUInt32 data_len,
+ SilcUInt32 encoding)
{
SilcBuffer buf;
SilcUInt32 len;
/* Saves public key into file */
-int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
- SilcUInt32 encoding)
+bool silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
+ SilcUInt32 encoding)
{
unsigned char *data;
SilcUInt32 data_len;
+ bool ret;
data = silc_pkcs_public_key_encode(public_key, &data_len);
- return silc_pkcs_save_public_key_internal(filename, data, data_len,
- encoding);
+ ret = silc_pkcs_save_public_key_internal(filename, data, data_len,
+ encoding);
+ silc_free(data);
+ return ret;
}
/* Saves public key into file */
-int silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 encoding)
+bool silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
+ SilcUInt32 data_len,
+ SilcUInt32 encoding)
{
return silc_pkcs_save_public_key_internal(filename, data, data_len,
encoding);
}
+#define SILC_PKCS_PRIVATE_KEY_MAGIC 0x738df531
+
/* Internal routine to save private key. */
-static int silc_pkcs_save_private_key_internal(char *filename,
- unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 encoding)
+static bool silc_pkcs_save_private_key_internal(char *filename,
+ unsigned char *data,
+ SilcUInt32 data_len,
+ unsigned char *key,
+ SilcUInt32 key_len,
+ SilcUInt32 encoding)
{
- SilcBuffer buf;
- SilcUInt32 len;
+ SilcCipher aes;
+ SilcHash md5;
+ SilcHmac sha1hmac;
+ SilcBuffer buf, enc;
+ SilcUInt32 len, blocklen;
+ unsigned char tmp[32], keymat[32];
+ int i;
- switch(encoding) {
+ memset(tmp, 0, sizeof(tmp));
+ memset(keymat, 0, sizeof(keymat));
+
+ /* Allocate the AES cipher */
+ if (!silc_cipher_alloc("aes-256-cbc", &aes)) {
+ SILC_LOG_ERROR(("Could not allocate AES cipher, probably not registered"));
+ return FALSE;
+ }
+ blocklen = silc_cipher_get_block_len(aes);
+ if (blocklen * 2 > sizeof(tmp))
+ return FALSE;
+
+ /* Allocate MD5 hash */
+ if (!silc_hash_alloc("md5", &md5)) {
+ SILC_LOG_ERROR(("Could not allocate MD5 hash, probably not registered"));
+ silc_cipher_free(aes);
+ return FALSE;
+ }
+
+ /* Allocate HMAC */
+ if (!silc_hmac_alloc("hmac-sha1-96", NULL, &sha1hmac)) {
+ SILC_LOG_ERROR(("Could not allocate SHA1 HMAC, probably not registered"));
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+ return FALSE;
+ }
+
+ /* Derive the encryption key from the provided key material. The key
+ is 256 bits length, and derived by taking hash of the data, then
+ re-hashing the data and the previous digest, and using the first and
+ second digest as the key. */
+ silc_hash_init(md5);
+ silc_hash_update(md5, key, key_len);
+ silc_hash_final(md5, keymat);
+ silc_hash_init(md5);
+ silc_hash_update(md5, key, key_len);
+ silc_hash_update(md5, keymat, 16);
+ silc_hash_final(md5, keymat + 16);
+
+ /* Set the key to the cipher */
+ silc_cipher_set_key(aes, keymat, sizeof(keymat) * 8);
+
+ /* Encode the buffer to be encrypted. Add padding to it too, at least
+ block size of the cipher. */
+
+ /* Allocate buffer for encryption */
+ len = silc_hmac_len(sha1hmac);
+ enc = silc_buffer_alloc_size(data_len + 4 + 4 +
+ (blocklen + (data_len % blocklen)) + len);
+ if (!enc) {
+ silc_hmac_free(sha1hmac);
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+ return FALSE;
+ }
+
+ /* Generate padding */
+ for (i = 0; i < blocklen + (data_len % blocklen); i++)
+ tmp[i] = silc_rng_global_get_byte_fast();
+
+ /* Put magic number */
+ SILC_PUT32_MSB(SILC_PKCS_PRIVATE_KEY_MAGIC, enc->data);
+ silc_buffer_pull(enc, 4);
+
+ /* Encode the buffer */
+ silc_buffer_format(enc,
+ SILC_STR_UI_INT(data_len),
+ SILC_STR_UI_XNSTRING(data, data_len),
+ SILC_STR_UI_XNSTRING(tmp, blocklen + (data_len %
+ blocklen)),
+ SILC_STR_END);
+
+ /* Encrypt. */
+ silc_cipher_encrypt(aes, enc->data, enc->data, enc->len - len,
+ silc_cipher_get_iv(aes));
+
+ silc_buffer_push(enc, 4);
+
+ /* Compute HMAC over the encrypted data and append the MAC to data.
+ The key is the first digest of the original key material. */
+ data_len = enc->len - len;
+ silc_hmac_init_with_key(sha1hmac, keymat, 16);
+ silc_hmac_update(sha1hmac, enc->data, data_len);
+ silc_buffer_pull(enc, data_len);
+ silc_hmac_final(sha1hmac, enc->data, NULL);
+ silc_buffer_push(enc, data_len);
+
+ /* Cleanup */
+ memset(keymat, 0, sizeof(keymat));
+ memset(tmp, 0, sizeof(tmp));
+ silc_hmac_free(sha1hmac);
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+
+ data = enc->data;
+ data_len = enc->len;
+
+ switch (encoding) {
case SILC_PKCS_FILE_BIN:
break;
case SILC_PKCS_FILE_PEM:
break;
}
+ /* Encode the data and save to file */
len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
- buf = silc_buffer_alloc(len);
- silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
-
+ buf = silc_buffer_alloc_size(len);
silc_buffer_format(buf,
SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN),
SILC_STR_UI_XNSTRING(data, data_len),
/* Save into a file */
if (silc_file_writefile_mode(filename, buf->data, buf->len, 0600)) {
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
+ silc_buffer_clear(enc);
+ silc_buffer_free(enc);
return FALSE;
}
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
+ silc_buffer_clear(enc);
+ silc_buffer_free(enc);
return TRUE;
}
/* Saves private key into file. */
-/* XXX The buffer should be encrypted if passphrase is provided. */
-int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key,
- unsigned char *passphrase,
- SilcUInt32 encoding)
+bool silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key,
+ unsigned char *passphrase,
+ SilcUInt32 passphrase_len,
+ SilcUInt32 encoding)
{
unsigned char *data;
SilcUInt32 data_len;
+ bool ret;
data = silc_pkcs_private_key_encode(private_key, &data_len);
- return silc_pkcs_save_private_key_internal(filename, data, data_len,
- encoding);
-}
-
-/* Saves private key into file. */
-/* XXX The buffer should be encrypted if passphrase is provided. */
-
-int silc_pkcs_save_private_key_data(char *filename, unsigned char *data,
- SilcUInt32 data_len,
- unsigned char *passphrase,
- SilcUInt32 encoding)
-{
- return silc_pkcs_save_private_key_internal(filename, data, data_len,
- encoding);
+ ret = silc_pkcs_save_private_key_internal(filename, data, data_len,
+ passphrase, passphrase_len,
+ encoding);
+ memset(data, 0, data_len);
+ silc_free(data);
+ return ret;
}
/* Loads public key from file and allocates new public key. Returns TRUE
- is loading was successful. */
+ if loading was successful. */
-int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
- SilcUInt32 encoding)
+bool silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
+ SilcUInt32 encoding)
{
unsigned char *cp, *old, *data, byte;
SilcUInt32 i, data_len, len;
/* Load private key from file and allocates new private key. Returns TRUE
if loading was successful. */
-/* XXX Should support encrypted private key files */
-int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
- SilcUInt32 encoding)
+bool silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
+ unsigned char *passphrase,
+ SilcUInt32 passphrase_len,
+ SilcUInt32 encoding)
{
+ SilcCipher aes;
+ SilcHash md5;
+ SilcHmac sha1hmac;
+ SilcUInt32 blocklen;
+ unsigned char tmp[32], keymat[32];
unsigned char *cp, *old, *data, byte;
- SilcUInt32 i, data_len, len;
+ SilcUInt32 i, data_len, len, magic, mac_len;
old = data = silc_file_readfile(filename, &data_len);
if (!data)
data = cp;
/* Decode private key */
- if (private_key) {
- len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
- strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+ len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
+ strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
- switch(encoding) {
- case SILC_PKCS_FILE_BIN:
- break;
- case SILC_PKCS_FILE_PEM:
- data = silc_pem_decode(data, len, &len);
- break;
+ switch(encoding) {
+ case SILC_PKCS_FILE_BIN:
+ break;
+ case SILC_PKCS_FILE_PEM:
+ data = silc_pem_decode(data, len, &len);
+ if (!data) {
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
}
+ break;
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ memset(keymat, 0, sizeof(keymat));
- if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) {
+ /* Private key files without the specific magic number are assumed
+ to be the old-style private keys that are not encrypted. */
+ SILC_GET32_MSB(magic, data);
+ if (magic != SILC_PKCS_PRIVATE_KEY_MAGIC) {
+ SILC_LOG_DEBUG(("Private key does not have correct magic!"));
+
+ /* Now decode the actual private key */
+ if (!silc_pkcs_private_key_decode(data, len, private_key)) {
memset(old, 0, data_len);
silc_free(old);
return FALSE;
}
+
+ memset(old, 0, data_len);
+ silc_free(old);
+ return TRUE;
+ }
+
+ /* Allocate the AES cipher */
+ if (!silc_cipher_alloc("aes-256-cbc", &aes)) {
+ SILC_LOG_ERROR(("Could not allocate AES cipher, probably not registered"));
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+ blocklen = silc_cipher_get_block_len(aes);
+ if (blocklen * 2 > sizeof(tmp)) {
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+
+ /* Allocate MD5 hash */
+ if (!silc_hash_alloc("md5", &md5)) {
+ SILC_LOG_ERROR(("Could not allocate MD5 hash, probably not registered"));
+ silc_cipher_free(aes);
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+
+ /* Allocate HMAC */
+ if (!silc_hmac_alloc("hmac-sha1-96", NULL, &sha1hmac)) {
+ SILC_LOG_ERROR(("Could not allocate SHA1 HMAC, probably not registered"));
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+
+ /* Derive the decryption key from the provided key material. The key
+ is 256 bits length, and derived by taking hash of the data, then
+ re-hashing the data and the previous digest, and using the first and
+ second digest as the key. */
+ silc_hash_init(md5);
+ silc_hash_update(md5, passphrase, passphrase_len);
+ silc_hash_final(md5, keymat);
+ silc_hash_init(md5);
+ silc_hash_update(md5, passphrase, passphrase_len);
+ silc_hash_update(md5, keymat, 16);
+ silc_hash_final(md5, keymat + 16);
+
+ /* Set the key to the cipher */
+ silc_cipher_set_key(aes, keymat, sizeof(keymat) * 8);
+
+ /* First, verify the MAC of the private key data */
+ mac_len = silc_hmac_len(sha1hmac);
+ silc_hmac_init_with_key(sha1hmac, keymat, 16);
+ silc_hmac_update(sha1hmac, data, len - mac_len);
+ silc_hmac_final(sha1hmac, tmp, NULL);
+ if (memcmp(tmp, data + (len - mac_len), mac_len)) {
+ SILC_LOG_DEBUG(("Integrity check for private key failed"));
+ memset(keymat, 0, sizeof(keymat));
+ memset(tmp, 0, sizeof(tmp));
+ silc_hmac_free(sha1hmac);
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+ data += 4;
+ len -= 4;
+
+ /* Decrypt the private key buffer */
+ silc_cipher_decrypt(aes, data, data, len - mac_len, silc_cipher_get_iv(aes));
+ SILC_GET32_MSB(i, data);
+ if (i > len) {
+ SILC_LOG_DEBUG(("Bad private key length in buffer!"));
+ memset(keymat, 0, sizeof(keymat));
+ memset(tmp, 0, sizeof(tmp));
+ silc_hmac_free(sha1hmac);
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
+ }
+ data += 4;
+ len = i;
+
+ /* Cleanup */
+ memset(keymat, 0, sizeof(keymat));
+ memset(tmp, 0, sizeof(tmp));
+ silc_hmac_free(sha1hmac);
+ silc_hash_free(md5);
+ silc_cipher_free(aes);
+
+ /* Now decode the actual private key */
+ if (!silc_pkcs_private_key_decode(data, len, private_key)) {
+ memset(old, 0, data_len);
+ silc_free(old);
+ return FALSE;
}
memset(old, 0, data_len);