mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
bc8ce928d4
Wipe temporary buffers and sha256 contexts so that no remnants of the password is left on the stack This ensures that the password is not leaked if some function reads the stack afterwards.
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 uint8_t *password, size_t password_len,
|
|
const uint8_t *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));
|
|
}
|