Merge pull request #15199 from fjmolinas/pbkdf2-sha256
sys/hashes/pbkdf2: Add PBKDF2-sha256 implementation. [TAKEOVER]
This commit is contained in:
commit
568e1e3635
@ -738,6 +738,10 @@ ifneq (,$(filter random,$(USEMODULE)))
|
|||||||
USEMODULE += luid
|
USEMODULE += luid
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter hashes,$(USEMODULE)))
|
||||||
|
USEMODULE += crypto
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter asymcute,$(USEMODULE)))
|
ifneq (,$(filter asymcute,$(USEMODULE)))
|
||||||
USEMODULE += sock_udp
|
USEMODULE += sock_udp
|
||||||
USEMODULE += sock_util
|
USEMODULE += sock_util
|
||||||
|
|||||||
112
sys/hashes/pbkdf2.c
Normal file
112
sys/hashes/pbkdf2.c
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
59
sys/include/hashes/pbkdf2.h
Normal file
59
sys/include/hashes/pbkdf2.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @defgroup sys_hashes_pbkdf2 PBKDF2
|
||||||
|
* @ingroup sys_hashes
|
||||||
|
* @brief PBKDF2 key derivation implementation.
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief PBKDF2 key derivation implementation.
|
||||||
|
*
|
||||||
|
* @author Juan I Carrano <j.carrano@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HASHES_PBKDF2_H
|
||||||
|
#define HASHES_PBKDF2_H
|
||||||
|
|
||||||
|
#include "hashes/sha256.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PBKDF2 key size length
|
||||||
|
*
|
||||||
|
* @note Currently only one derived key length is supported (32)
|
||||||
|
*/
|
||||||
|
#define PBKDF2_KEY_SIZE SHA256_DIGEST_LENGTH
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a key from a password and hash using PBKDF2.
|
||||||
|
*
|
||||||
|
* @param[in] password password pointer
|
||||||
|
* @param[in] password_len length of password
|
||||||
|
* @param[in] salt salt pointer
|
||||||
|
* @param[in] salt_len salt length, recommended 64bit
|
||||||
|
* @param[in] iterations number of rounds. Must be >1.
|
||||||
|
* NIST’s detailed guide (Appendix A.2.2),
|
||||||
|
* 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,
|
||||||
|
int iterations,
|
||||||
|
uint8_t *output);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HASHES_PBKDF2_H */
|
||||||
11
tests/pbkdf2/Makefile
Normal file
11
tests/pbkdf2/Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
7
tests/pbkdf2/Makefile.ci
Normal file
7
tests/pbkdf2/Makefile.ci
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
BOARD_INSUFFICIENT_MEMORY := \
|
||||||
|
arduino-duemilanove \
|
||||||
|
arduino-nano \
|
||||||
|
arduino-uno \
|
||||||
|
atmega328p \
|
||||||
|
nucleo-l011k4 \
|
||||||
|
#
|
||||||
11
tests/pbkdf2/README
Normal file
11
tests/pbkdf2/README
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Test PBKDF2 implementation
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This test evaluates the RIOT implementation against a reference. The objective
|
||||||
|
is flexibility and clarity, and for this reason there are no hard coded vectors,
|
||||||
|
but instead the test is interactive, with the DUT processing vectors given
|
||||||
|
through the serial interface.
|
||||||
|
|
||||||
|
This means that the test is slower, but more complete and trustworthy.
|
||||||
|
|
||||||
|
The test is completely automated.
|
||||||
146
tests/pbkdf2/main.c
Normal file
146
tests/pbkdf2/main.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Test PBKDF2-sha256 implementation.
|
||||||
|
*
|
||||||
|
* @author Juan Carrano <j.carrano@fu-berlin.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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "hashes/pbkdf2.h"
|
||||||
|
|
||||||
|
const char error_message[] = "{error}";
|
||||||
|
const char input_message[] = "{ready}";
|
||||||
|
|
||||||
|
#define LINEBUF_SZ (128)
|
||||||
|
|
||||||
|
enum TEST_STATE {
|
||||||
|
TEST_READ_PASS,
|
||||||
|
TEST_READ_SALT,
|
||||||
|
TEST_READ_ITERS,
|
||||||
|
TEST_COMPUTE,
|
||||||
|
TEST_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void _clear_input(void)
|
||||||
|
{
|
||||||
|
/* clear input buffer */
|
||||||
|
int c;
|
||||||
|
while ( (c = getchar()) != '\n' && c != EOF ) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
static char linebuf[LINEBUF_SZ];
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
42
tests/pbkdf2/tests/01-rfc.py
Executable file
42
tests/pbkdf2/tests/01-rfc.py
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
#!/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)
|
||||||
80
tests/pbkdf2/tests/02-random.py
Executable file
80
tests/pbkdf2/tests/02-random.py
Executable file
@ -0,0 +1,80 @@
|
|||||||
|
#!/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)
|
||||||
45
tests/pbkdf2/tests/test_base.py
Normal file
45
tests/pbkdf2/tests/test_base.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# 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 os
|
||||||
|
import sys
|
||||||
|
import base64
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
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(l):
|
||||||
|
assert len(l) < MAX_LINE
|
||||||
|
_safe_expect_exact('{ready}')
|
||||||
|
child.sendline(l)
|
||||||
|
|
||||||
|
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.path.append(os.path.join(os.environ['RIOTTOOLS'], 'testrunner'))
|
||||||
|
from testrunner import run
|
||||||
|
sys.exit(run(partial(test, vectors)))
|
||||||
Loading…
x
Reference in New Issue
Block a user