mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 04:12:47 +01:00
Merge pull request #18932 from maribu/sys/hashes/pbkdf2
tests/pbkdf2: de-flanky-fy test
This commit is contained in:
commit
e0688991dd
@ -43,8 +43,8 @@ static void inplace_xor_digests(uint8_t *d1, const uint8_t *d2)
|
||||
}
|
||||
}
|
||||
|
||||
void pbkdf2_sha256(const uint8_t *password, size_t password_len,
|
||||
const uint8_t *salt, size_t salt_len,
|
||||
void pbkdf2_sha256(const void *password, size_t password_len,
|
||||
const void *salt, size_t salt_len,
|
||||
int iterations,
|
||||
uint8_t *output)
|
||||
{
|
||||
|
@ -47,8 +47,8 @@ extern "C" {
|
||||
* recommended 10000
|
||||
* @param[out] output array of size PBKDF2_KEY_SIZE
|
||||
*/
|
||||
void pbkdf2_sha256(const uint8_t *password, size_t password_len,
|
||||
const uint8_t *salt, size_t salt_len,
|
||||
void pbkdf2_sha256(const void *password, size_t password_len,
|
||||
const void *salt, size_t salt_len,
|
||||
int iterations,
|
||||
uint8_t *output);
|
||||
|
||||
|
@ -1,19 +1,6 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# This application uses getchar and thus expects input from stdio
|
||||
USEMODULE += stdin
|
||||
USEMODULE += hashes
|
||||
USEMODULE += base64
|
||||
|
||||
# Use a terminal that does not introduce extra characters into the stream.
|
||||
RIOT_TERMINAL ?= socat
|
||||
|
||||
#ensure the rx buffer has some room even with large test patterns
|
||||
CFLAGS += -DSTDIO_UART_RX_BUFSIZE=128
|
||||
USEMODULE += fmt
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# Increase Stack size for AVR
|
||||
ifneq (,$(filter avr8_common,$(USEMODULE)))
|
||||
CFLAGS += -DTHREAD_STACKSIZE_MAIN=THREAD_STACKSIZE_LARGE
|
||||
endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin.
|
||||
* 2022 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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
|
||||
@ -13,132 +14,104 @@
|
||||
* @brief Test PBKDF2-sha256 implementation.
|
||||
*
|
||||
* @author Juan Carrano <j.carrano@fu-berlin.de>
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*
|
||||
* This application reads (password, salt, iterations) tuples from the
|
||||
* standard input and outputs the derived key.
|
||||
*
|
||||
* The salt must be base64 encoded. The key is printed as base64.
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "base64.h"
|
||||
#include "container.h"
|
||||
#include "fmt.h"
|
||||
#include "hashes/pbkdf2.h"
|
||||
#include "hashes/sha256.h"
|
||||
#include "kernel_defines.h"
|
||||
|
||||
const char error_message[] = "{error}";
|
||||
const char input_message[] = "{ready}";
|
||||
static uint8_t key[SHA256_DIGEST_LENGTH];
|
||||
|
||||
#define LINEBUF_SZ (128)
|
||||
|
||||
enum TEST_STATE {
|
||||
TEST_READ_PASS,
|
||||
TEST_READ_SALT,
|
||||
TEST_READ_ITERS,
|
||||
TEST_COMPUTE,
|
||||
TEST_ERROR
|
||||
struct testcase {
|
||||
const char *password;
|
||||
const char *salt;
|
||||
uint16_t iterations;
|
||||
const uint8_t digest[sizeof(key)];
|
||||
};
|
||||
|
||||
static void _clear_input(void)
|
||||
{
|
||||
/* clear input buffer */
|
||||
int c;
|
||||
while ( (c = getchar()) != '\n' && c != EOF ) { }
|
||||
}
|
||||
struct testcase testcases[] = {
|
||||
{
|
||||
.password = "passwd",
|
||||
.salt = "salt",
|
||||
.iterations = 1,
|
||||
/* dig = hashlib.pbkdf2_hmac("sha256", "passwd".encode("utf-8"),
|
||||
* "salt".encode("utf-8"), 1)
|
||||
* "".join("0x{:02x}, ".format(b) for b in dig)
|
||||
*/
|
||||
.digest = {
|
||||
0x55, 0xac, 0x04, 0x6e, 0x56, 0xe3, 0x08, 0x9f,
|
||||
0xec, 0x16, 0x91, 0xc2, 0x25, 0x44, 0xb6, 0x05,
|
||||
0xf9, 0x41, 0x85, 0x21, 0x6d, 0xde, 0x04, 0x65,
|
||||
0xe6, 0x8b, 0x9d, 0x57, 0xc2, 0x0d, 0xac, 0xbc,
|
||||
}
|
||||
},
|
||||
{
|
||||
.password = "RIOT",
|
||||
.salt = "rocks",
|
||||
.iterations = 16,
|
||||
/* dig = hashlib.pbkdf2_hmac("sha256", "RIOT".encode("utf-8"),
|
||||
* "rocks".encode("utf-8"), 16)
|
||||
* "".join("0x{:02x}, ".format(b) for b in dig)
|
||||
*/
|
||||
.digest = {
|
||||
0x72, 0xa6, 0x06, 0xbb, 0x5c, 0xbe, 0x92, 0x4a,
|
||||
0xd2, 0x0a, 0xee, 0xc2, 0x4e, 0xa5, 0x17, 0xc4,
|
||||
0xd7, 0xb1, 0x1d, 0x04, 0x9d, 0x84, 0xbb, 0x29,
|
||||
0x6b, 0x36, 0xad, 0x90, 0x4d, 0x6f, 0x79, 0xdf,
|
||||
}
|
||||
},
|
||||
{
|
||||
.password = "This is a secure password", /* <-- no it is NOT! */
|
||||
.salt = "and this salt is even more secure",
|
||||
.iterations = 13,
|
||||
/* dig = hashlib.pbkdf2_hmac("sha256",
|
||||
* "This is a secure password".encode("utf-8"),
|
||||
* "and this salt is even more secure".encode("utf-8"),
|
||||
* 13)
|
||||
* "".join("0x{:02x}, ".format(b) for b in dig)
|
||||
*/
|
||||
.digest = {
|
||||
0x9a, 0x41, 0x83, 0x2b, 0x77, 0xc4, 0x61, 0x64,
|
||||
0x06, 0xd3, 0x2e, 0x97, 0x06, 0x5e, 0xc5, 0xc7,
|
||||
0xe1, 0xa0, 0x18, 0x75, 0x01, 0xfe, 0xb8, 0xc8,
|
||||
0x70, 0x92, 0x28, 0x0e, 0x1d, 0x1a, 0x00, 0xb6,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static char linebuf[LINEBUF_SZ];
|
||||
bool failed = false;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
|
||||
struct testcase *tc = &testcases[i];
|
||||
size_t password_len = strlen(tc->password);
|
||||
size_t salt_len = strlen(tc->salt);
|
||||
memset(key, 0x00, sizeof(key));
|
||||
pbkdf2_sha256(tc->password, password_len, tc->salt, salt_len,
|
||||
tc->iterations, key);
|
||||
|
||||
/* There will be a few bytes wasted here */
|
||||
static char password[LINEBUF_SZ];
|
||||
static uint8_t salt[LINEBUF_SZ];
|
||||
static uint8_t key[PBKDF2_KEY_SIZE];
|
||||
|
||||
size_t passwd_len = 0, salt_len = 0;
|
||||
int iterations = 0;
|
||||
|
||||
enum TEST_STATE state = TEST_READ_PASS;
|
||||
|
||||
_clear_input();
|
||||
|
||||
while ((puts(input_message), fgets(linebuf, LINEBUF_SZ, stdin) != NULL)) {
|
||||
char *s_end;
|
||||
int conversion_status, line_len = strlen(linebuf)-1;
|
||||
size_t b64_buff_size;
|
||||
|
||||
linebuf[line_len] = '\0';
|
||||
|
||||
switch (state) {
|
||||
case TEST_READ_PASS:
|
||||
strcpy(password, linebuf);
|
||||
passwd_len = line_len;
|
||||
state++;
|
||||
|
||||
break;
|
||||
case TEST_READ_SALT:
|
||||
/* work around bug in base64_decode */
|
||||
if (line_len == 0) {
|
||||
salt_len = 0;
|
||||
conversion_status = BASE64_SUCCESS;
|
||||
} else {
|
||||
salt_len = sizeof(salt);
|
||||
conversion_status = base64_decode((uint8_t*)linebuf,
|
||||
line_len+1,
|
||||
salt, &salt_len);
|
||||
}
|
||||
|
||||
if (conversion_status == BASE64_SUCCESS) {
|
||||
state++;
|
||||
} else {
|
||||
state = TEST_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
case TEST_READ_ITERS:
|
||||
iterations = strtol(linebuf, &s_end, 10);
|
||||
|
||||
if (*s_end != '\0') {
|
||||
state = TEST_ERROR;
|
||||
} else {
|
||||
state++;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
assert(1);
|
||||
break;
|
||||
if (memcmp(tc->digest, key, sizeof(key)) != 0) {
|
||||
failed = true;
|
||||
print_str("Test vector ");
|
||||
print_u32_dec((uint32_t)i);
|
||||
print_str(": FAILED\n");
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case TEST_COMPUTE:
|
||||
pbkdf2_sha256((uint8_t*)password, passwd_len, salt, salt_len,
|
||||
iterations, key);
|
||||
}
|
||||
|
||||
b64_buff_size = sizeof(linebuf);
|
||||
conversion_status = base64_encode(key, sizeof(key),
|
||||
(uint8_t*)linebuf,
|
||||
&b64_buff_size);
|
||||
|
||||
if (conversion_status == BASE64_SUCCESS) {
|
||||
linebuf[b64_buff_size] = 0;
|
||||
puts(linebuf);
|
||||
} else {
|
||||
puts(error_message);
|
||||
}
|
||||
|
||||
state = TEST_READ_PASS;
|
||||
break;
|
||||
case TEST_ERROR:
|
||||
puts(error_message);
|
||||
state = TEST_READ_PASS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!failed) {
|
||||
print_str("TEST PASSED\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
# Author: Juan Carrano <j.carrano@fu-berlin.de>
|
||||
"""Vector from RFC 7914 section 11"""
|
||||
|
||||
import os
|
||||
|
||||
import hashlib
|
||||
import test_base
|
||||
|
||||
KEY_SIZE = hashlib.sha256().digest_size
|
||||
|
||||
v_easy = """55 ac 04 6e 56 e3 08 9f ec 16 91 c2 25 44 b6 05
|
||||
f9 41 85 21 6d de 04 65 e6 8b 9d 57 c2 0d ac bc
|
||||
49 ca 9c cc f1 79 b6 45 99 16 64 b3 9d 77 ef 31
|
||||
7c 71 b8 45 b1 e3 0b d5 09 11 20 41 d3 a1 97 83"""
|
||||
|
||||
v_hard = """
|
||||
4d dc d8 f6 0b 98 be 21 83 0c ee 5e f2 27 01 f9
|
||||
64 1a 44 18 d0 4c 04 14 ae ff 08 87 6b 34 ab 56
|
||||
a1 d4 25 a1 22 58 33 54 9a db 84 1b 51 c9 b3 17
|
||||
6a 27 2b de bb a1 d0 78 47 8f 62 b3 97 f3 3c 8d"""
|
||||
|
||||
|
||||
def process_octets(s):
|
||||
return bytes(int(x, 16) for x in s.split())[:KEY_SIZE]
|
||||
|
||||
|
||||
VECTORS = [
|
||||
('passwd', b"salt", 1, process_octets(v_easy))
|
||||
]
|
||||
|
||||
if os.environ.get('BOARD') == 'native':
|
||||
VECTORS.append(("Password", b"NaCl", 80000, process_octets(v_hard)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_base.main(VECTORS)
|
20
tests/pbkdf2/tests/01-run.py
Executable file
20
tests/pbkdf2/tests/01-run.py
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
#
|
||||
# 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.
|
||||
|
||||
# @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect("TEST PASSED")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
# Author: Juan Carrano <j.carrano@fu-berlin.de>
|
||||
"""Random test vectors"""
|
||||
|
||||
from bisect import bisect as _bisect
|
||||
from itertools import accumulate as _accumulate
|
||||
import hashlib
|
||||
import random as rand
|
||||
|
||||
import test_base
|
||||
|
||||
|
||||
_pass_chars = [c for c in (chr(x) for x in range(128))
|
||||
if c.isprintable()]
|
||||
|
||||
|
||||
class random2(rand.Random):
|
||||
# Murdock uses python 3.5 where random.choices is not available, this
|
||||
# is a verbatim copy from python 3.6
|
||||
def choices(self, population, weights=None, *, cum_weights=None, k=1):
|
||||
"""Return a k sized list of population elements chosen with replacement.
|
||||
If the relative weights or cumulative weights are not specified,
|
||||
the selections are made with equal probability.
|
||||
"""
|
||||
random = self.random
|
||||
if cum_weights is None:
|
||||
if weights is None:
|
||||
_int = int
|
||||
total = len(population)
|
||||
return [population[_int(random() * total)] for i in range(k)]
|
||||
cum_weights = list(_accumulate(weights))
|
||||
elif weights is not None:
|
||||
raise TypeError('Cannot specify both weights and cumulative weights')
|
||||
if len(cum_weights) != len(population):
|
||||
raise ValueError('The number of weights does not match the population')
|
||||
bisect = _bisect.bisect
|
||||
total = cum_weights[-1]
|
||||
hi = len(cum_weights) - 1
|
||||
return [population[bisect(cum_weights, random() * total, 0, hi)]
|
||||
for i in range(k)]
|
||||
|
||||
|
||||
randgen = random2(42)
|
||||
|
||||
|
||||
def randompass(length):
|
||||
return "".join(randgen.choices(_pass_chars, k=length))
|
||||
|
||||
|
||||
def randomsalt(bytes_):
|
||||
return (randgen.getrandbits(bytes_*8).to_bytes(bytes_, 'big')
|
||||
if bytes_ else b'')
|
||||
|
||||
|
||||
def randomvector(pass_len, salt_len, iters):
|
||||
pass_ = randompass(pass_len)
|
||||
salt = randomsalt(salt_len)
|
||||
key = hashlib.pbkdf2_hmac('sha256', pass_.encode('ascii'), salt, iters)
|
||||
|
||||
return pass_, salt, iters, key
|
||||
|
||||
|
||||
VECTORS = [
|
||||
randomvector(0, 16, 10),
|
||||
randomvector(8, 0, 10),
|
||||
randomvector(9, 64, 1),
|
||||
randomvector(65, 38, 20),
|
||||
randomvector(32, 15, 12),
|
||||
randomvector(48, 32, 15),
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_base.main(VECTORS)
|
@ -1,44 +0,0 @@
|
||||
# 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.
|
||||
#
|
||||
# Author: Juan Carrano <j.carrano@fu-berlin.de>
|
||||
|
||||
import sys
|
||||
import base64
|
||||
from functools import partial
|
||||
|
||||
from testrunner import run
|
||||
|
||||
MAX_LINE = 128
|
||||
|
||||
|
||||
def safe_encode(data):
|
||||
"""Empty lines will confuse the target, replace them with padding."""
|
||||
return base64.b64encode(data).decode('ascii') if data else ""
|
||||
|
||||
|
||||
def test(vectors, child):
|
||||
def _safe_expect_exact(s):
|
||||
idx = child.expect_exact([s+'\r\n', '{error}\r\n'])
|
||||
assert idx == 0
|
||||
return idx
|
||||
|
||||
def _safe_sendline(line):
|
||||
assert len(line) < MAX_LINE
|
||||
_safe_expect_exact('{ready}')
|
||||
child.sendline(line)
|
||||
|
||||
for passwd, salt, iters, key in vectors:
|
||||
_safe_sendline(passwd)
|
||||
_safe_sendline(safe_encode(salt))
|
||||
_safe_sendline(str(iters))
|
||||
|
||||
expected_key = base64.b64encode(key).decode('ascii')
|
||||
_safe_expect_exact(expected_key)
|
||||
|
||||
|
||||
def main(vectors):
|
||||
sys.exit(run(partial(test, vectors)))
|
Loading…
Reference in New Issue
Block a user