/* minip12.c - A mini pkcs-12 implementation (modified for gnutls)
*
* Copyright (C) 2002-2012 Free Software Foundation, Inc.
*
* This file is part of GnuTLS.
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
#include "gnutls_int.h"
#include
#include "errors.h"
#include
#include
#define MAX_PASS_LEN 256
/* ID should be:
* 3 for MAC
* 2 for IV
* 1 for encryption key
*
* Note that this function produces different key for the
* NULL password, and for the password with zero length.
*/
int
_gnutls_pkcs12_string_to_key(const mac_entry_st * me,
unsigned int id, const uint8_t * salt,
unsigned int salt_size, unsigned int iter,
const char *pw, unsigned int req_keylen,
uint8_t * keybuf)
{
int rc;
unsigned int i, j;
digest_hd_st md;
bigint_t num_b1 = NULL, num_ij = NULL;
bigint_t mpi512 = NULL;
unsigned int pwlen;
uint8_t hash[MAX_HASH_SIZE], buf_b[64], buf_i[MAX_PASS_LEN + 64], *p;
uint8_t d[64];
size_t cur_keylen;
size_t n, m, p_size, i_size;
gnutls_datum_t ucs2 = {NULL, 0};
unsigned mac_len;
const uint8_t buf_512[] = /* 2^64 */
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
cur_keylen = 0;
if (me->block_size != 64)
return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
if (pw) {
pwlen = strlen(pw);
if (pwlen == 0) {
ucs2.data = gnutls_calloc(1, 2);
if (ucs2.data == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
ucs2.size = 2;
} else {
rc = _gnutls_utf8_to_ucs2(pw, pwlen, &ucs2);
if (rc < 0)
return gnutls_assert_val(rc);
/* include terminating zero */
ucs2.size+=2;
}
pwlen = ucs2.size;
pw = (char*)ucs2.data;
} else {
pwlen = 0;
}
if (pwlen > MAX_PASS_LEN) {
rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
goto cleanup;
}
rc = _gnutls_mpi_init_scan(&mpi512, buf_512, sizeof(buf_512));
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
/* Store salt and password in BUF_I */
p_size = ((pwlen / 64) * 64) + 64;
if (p_size > sizeof(buf_i) - 64) {
rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
goto cleanup;
}
if (salt_size > 0) {
p = buf_i;
for (i = 0; i < 64; i++)
*p++ = salt[i % salt_size];
} else {
memset(buf_i, 0, 64);
p = buf_i + 64;
}
if (pw) {
for (i = j = 0; i < p_size; i += 2) {
*p++ = pw[j];
*p++ = pw[j+1];
j+=2;
if (j >= pwlen)
j = 0;
}
} else {
memset(p, 0, p_size);
}
i_size = 64 + p_size;
mac_len = _gnutls_mac_get_algo_len(me);
assert(mac_len != 0);
for (;;) {
rc = _gnutls_hash_init(&md, me);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
memset(d, id & 0xff, 64);
_gnutls_hash(&md, d, 64);
_gnutls_hash(&md, buf_i, pw ? i_size : 64);
_gnutls_hash_deinit(&md, hash);
for (i = 1; i < iter; i++) {
rc = _gnutls_hash_fast((gnutls_digest_algorithm_t)me->id,
hash, mac_len, hash);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
}
for (i = 0; i < mac_len && cur_keylen < req_keylen; i++)
keybuf[cur_keylen++] = hash[i];
if (cur_keylen == req_keylen) {
rc = 0; /* ready */
goto cleanup;
}
/* need more bytes. */
for (i = 0; i < 64; i++)
buf_b[i] = hash[i % mac_len];
n = 64;
rc = _gnutls_mpi_init_scan(&num_b1, buf_b, n);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
rc = _gnutls_mpi_add_ui(num_b1, num_b1, 1);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
for (i = 0; i < 128; i += 64) {
n = 64;
rc = _gnutls_mpi_init_scan(&num_ij, buf_i + i, n);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
rc = _gnutls_mpi_addm(num_ij, num_ij, num_b1, mpi512);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
n = 64;
m = (_gnutls_mpi_get_nbits(num_ij) + 7) / 8;
memset(buf_i + i, 0, n - m);
rc = _gnutls_mpi_print(num_ij, buf_i + i + n - m,
&n);
if (rc < 0) {
gnutls_assert();
goto cleanup;
}
_gnutls_mpi_release(&num_ij);
}
}
cleanup:
_gnutls_mpi_release(&num_ij);
_gnutls_mpi_release(&num_b1);
_gnutls_mpi_release(&mpi512);
gnutls_free(ucs2.data);
return rc;
}