mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
e92a7164e3
Having to cast a password provided as `const char *` to `const uint8_t *` is a needless pain in the ass when using the API. Hence, fix it by accepting passwords and salts as `const void *` instead.
113 lines
3.1 KiB
C
113 lines
3.1 KiB
C
/*
|
|
* Copyright (C) 2019 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
/**
|
|
* @ingroup examples
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief PBKDF2 key derivation implementation- only sha256 is supported
|
|
* at the moment, and the key size is fixed.
|
|
*
|
|
* @author Juan I Carrano <j.carrano@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "hashes/sha256.h"
|
|
#include "hashes/pbkdf2.h"
|
|
#include "crypto/helper.h"
|
|
|
|
static void inplace_xor_scalar(uint8_t *bytes, size_t len, uint8_t c)
|
|
{
|
|
while (len--) {
|
|
*bytes ^= c;
|
|
bytes++;
|
|
}
|
|
}
|
|
|
|
static void inplace_xor_digests(uint8_t *d1, const uint8_t *d2)
|
|
{
|
|
int len = SHA256_DIGEST_LENGTH;
|
|
|
|
while (len--) {
|
|
*d1 ^= *d2;
|
|
d1++;
|
|
d2++;
|
|
}
|
|
}
|
|
|
|
void pbkdf2_sha256(const void *password, size_t password_len,
|
|
const void *salt, size_t salt_len,
|
|
int iterations,
|
|
uint8_t *output)
|
|
{
|
|
sha256_context_t inner;
|
|
sha256_context_t outer;
|
|
uint8_t tmp_digest[SHA256_DIGEST_LENGTH];
|
|
int first_iter = 1;
|
|
|
|
{
|
|
uint8_t processed_pass[SHA256_INTERNAL_BLOCK_SIZE] = {0};
|
|
|
|
if (password_len > sizeof(processed_pass)) {
|
|
sha256_init(&inner);
|
|
sha256_update(&inner, password, password_len);
|
|
sha256_final(&inner, processed_pass);
|
|
} else {
|
|
memcpy(processed_pass, password, password_len);
|
|
}
|
|
|
|
sha256_init(&inner);
|
|
sha256_init(&outer);
|
|
|
|
/* Trick: doing inner.update(processed_pass XOR 0x36) followed by
|
|
* inner.update(processed_pass XOR 0x5C) requires remembering
|
|
* processed_pass. Instead undo the first XOR while doing the second.
|
|
*/
|
|
inplace_xor_scalar(processed_pass, sizeof(processed_pass), 0x36);
|
|
sha256_update(&inner, processed_pass, sizeof(processed_pass));
|
|
|
|
inplace_xor_scalar(processed_pass, sizeof(processed_pass), 0x36 ^ 0x5C);
|
|
sha256_update(&outer, processed_pass, sizeof(processed_pass));
|
|
|
|
crypto_secure_wipe(&processed_pass, sizeof(processed_pass));
|
|
}
|
|
|
|
memset(output, 0, SHA256_DIGEST_LENGTH);
|
|
|
|
while (iterations--) {
|
|
sha256_context_t inner_copy = inner, outer_copy = outer;
|
|
|
|
if (first_iter) {
|
|
sha256_update(&inner_copy, salt, salt_len);
|
|
sha256_update(&inner_copy, "\x00\x00\x00\x01", 4);
|
|
first_iter = 0;
|
|
} else {
|
|
sha256_update(&inner_copy, tmp_digest, sizeof(tmp_digest));
|
|
}
|
|
|
|
sha256_final(&inner_copy, tmp_digest);
|
|
|
|
sha256_update(&outer_copy, tmp_digest, sizeof(tmp_digest));
|
|
sha256_final(&outer_copy, tmp_digest);
|
|
|
|
inplace_xor_digests(output, tmp_digest);
|
|
|
|
if (iterations == 0) {
|
|
crypto_secure_wipe(&inner_copy, sizeof(inner_copy));
|
|
crypto_secure_wipe(&outer_copy, sizeof(outer_copy));
|
|
}
|
|
}
|
|
|
|
crypto_secure_wipe(&inner, sizeof(inner));
|
|
crypto_secure_wipe(&outer, sizeof(outer));
|
|
crypto_secure_wipe(&tmp_digest, sizeof(tmp_digest));
|
|
}
|