| /* |
| * Copyright (C) 2013 Tobias Brunner |
| * HSR 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 <library.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) |
| { |
| uint16_t sum; |
| uint8_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, |
| uint64_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; |
| uint64_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, |
| uint64_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; |
| } |