mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
!fsutils/passwd: Replace TEA with PBKDF2-HMAC-SHA256
Migrate passwd encrypt/verify to PBKDF2 modular crypt format using kernel cryptodev (CRYPTO_PBKDF2_HMAC_SHA256 via /dev/crypto). Add passwd_pbkdf2 wrapper, base64url helpers, complexity validation, and pbkdf2_test for RFC 6070 vector coverage. FSUTILS_PASSWD selects CRYPTO, ALLOW_BSD_COMPONENTS, and CRYPTO_CRYPTODEV so existing sim defconfigs keep building. Change NSH_LOGIN_USERNAME default to root and remove fixed-login password defaults. BREAKING CHANGE: TEA-encoded /etc/passwd entries no longer verify. Regenerate each entry after upgrading. Pair with the nuttx host mkpasswd changes in apache/nuttx#19209. Boards must enable the appropriate software or hardware crypto backend for PBKDF2 at runtime. When CONFIG_NSH_LOGIN_FIXED=y, set CONFIG_NSH_LOGIN_PASSWORD in the board defconfig or menuconfig; there is no default password. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
parent
01c6a60581
commit
608f13fd4b
22 changed files with 1096 additions and 226 deletions
|
|
@ -23,7 +23,8 @@
|
|||
if(CONFIG_FSUTILS_PASSWD)
|
||||
set(CSRCS)
|
||||
|
||||
list(APPEND CSRCS passwd_verify.c passwd_find.c passwd_encrypt.c)
|
||||
list(APPEND CSRCS passwd_verify.c passwd_find.c passwd_encrypt.c
|
||||
passwd_pbkdf2.c)
|
||||
|
||||
if(NOT CONFIG_FSUTILS_PASSWD_READONLY)
|
||||
list(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,19 @@
|
|||
config FSUTILS_PASSWD
|
||||
bool "Password file support"
|
||||
default n
|
||||
depends on CRYPTO_CRYPTODEV
|
||||
depends on NETUTILS_CODECS
|
||||
depends on CODECS_BASE64
|
||||
---help---
|
||||
Enables support for /etc/passwd file access routines
|
||||
Enables support for /etc/passwd file access routines.
|
||||
|
||||
Requires CONFIG_CRYPTO=y, CRYPTO_CRYPTODEV (and
|
||||
ALLOW_BSD_COMPONENTS), plus NETUTILS_CODECS/CODECS_BASE64 for
|
||||
base64url hash encoding.
|
||||
|
||||
NOTE: Password hashes use PBKDF2-HMAC-SHA256 (modular crypt format).
|
||||
Existing TEA-encrypted /etc/passwd entries are NOT compatible and
|
||||
must be regenerated.
|
||||
|
||||
if FSUTILS_PASSWD
|
||||
|
||||
|
|
@ -23,23 +34,14 @@ config FSUTILS_PASSWD_IOBUFFER_SIZE
|
|||
int "Allocated I/O buffer size"
|
||||
default 512
|
||||
|
||||
config FSUTILS_PASSWD_KEY1
|
||||
hex "Encryption key value 1"
|
||||
default 0
|
||||
config FSUTILS_PASSWD_PBKDF2_ITERATIONS
|
||||
int "Default PBKDF2 iteration count for new passwords"
|
||||
default 10000
|
||||
range 1000 200000
|
||||
---help---
|
||||
Leave at 0 if random key generation is enabled under Board
|
||||
Selection. Otherwise set all four keys to unique non-zero values.
|
||||
|
||||
config FSUTILS_PASSWD_KEY2
|
||||
hex "Encryption key value 2"
|
||||
default 0
|
||||
|
||||
config FSUTILS_PASSWD_KEY3
|
||||
hex "Encryption key value 3"
|
||||
default 0
|
||||
|
||||
config FSUTILS_PASSWD_KEY4
|
||||
hex "Encryption key value 4"
|
||||
default 0
|
||||
Number of PBKDF2-HMAC-SHA256 iterations applied when setting a new
|
||||
password. Higher values slow brute-force attacks but also increase
|
||||
login latency on low-MHz MCUs. The iteration count is stored in each
|
||||
hash string, so changing this option only affects newly-set passwords.
|
||||
|
||||
endif # FSUTILS_PASSWD
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ include $(APPDIR)/Make.defs
|
|||
|
||||
ifeq ($(CONFIG_FSUTILS_PASSWD),y)
|
||||
CSRCS += passwd_verify.c passwd_find.c passwd_encrypt.c
|
||||
CSRCS += passwd_pbkdf2.c
|
||||
ifneq ($(CONFIG_FSUTILS_PASSWD_READONLY),y)
|
||||
CSRCS += passwd_adduser.c passwd_deluser.c passwd_update.c passwd_append.c
|
||||
CSRCS += passwd_delete.c passwd_lock.c
|
||||
|
|
|
|||
|
|
@ -36,17 +36,18 @@
|
|||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MAX_ENCRYPTED 48 /* Maximum size of a password (encrypted, ASCII) */
|
||||
#define MAX_USERNAME 48 /* Maximum size of a username */
|
||||
#define MAX_RECORD (MAX_USERNAME + MAX_ENCRYPTED + 1)
|
||||
/* MCF format: $pbkdf2-sha256$<iter>$<salt>$<hash> */
|
||||
|
||||
/* The TEA incryption algorithm generates 8 bytes of encrypted data per
|
||||
* 8 bytes of unencrypted data. The encrypted presentation is base64 which
|
||||
* is 8-bits of ASCII for each 6 bits of data. That is a 3-to-4 expansion
|
||||
* ratio. MAX_ENCRYPTED must be a multiple of 8 bytes.
|
||||
*/
|
||||
#define PASSWD_MCF_PREFIX "$pbkdf2-sha256$"
|
||||
#define PASSWD_SALT_BYTES 16
|
||||
#define PASSWD_HASH_BYTES 32
|
||||
|
||||
#define MAX_PASSWORD (3 * MAX_ENCRYPTED / 4)
|
||||
/* 15 + 6 + 1 + 22 + 1 + 43 = 88 bytes for default parameters */
|
||||
|
||||
#define MAX_ENCRYPTED 96
|
||||
#define MAX_USERNAME 48
|
||||
#define MAX_RECORD (MAX_USERNAME + MAX_ENCRYPTED + 1)
|
||||
#define MAX_PASSWORD 256
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
struct passwd_s
|
||||
{
|
||||
off_t offset; /* File offset (start of record) */
|
||||
char encrypted[MAX_ENCRYPTED + 1]; /* Encrtyped password in file */
|
||||
char encrypted[MAX_ENCRYPTED + 1]; /* Password hash in file */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -94,10 +95,11 @@ void passwd_unlock(FAR sem_t *sem);
|
|||
* Name: passwd_encrypt
|
||||
*
|
||||
* Description:
|
||||
* Encrypt a password. Currently uses the Tiny Encryption Algorithm.
|
||||
* Hash a password with PBKDF2-HMAC-SHA256 and encode the result in modular
|
||||
* crypt format for storage in /etc/passwd.
|
||||
*
|
||||
* Input Parameters:
|
||||
* password -- The password string to be encrypted
|
||||
* password -- The password string to be hashed
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
||||
|
|
|
|||
|
|
@ -25,85 +25,145 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/random.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nuttx/crypto/tea.h>
|
||||
#include <netutils/base64.h>
|
||||
|
||||
#include "passwd.h"
|
||||
#include "passwd_pbkdf2.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* This should be better protected */
|
||||
#ifndef CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS
|
||||
# define CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS 10000
|
||||
#endif
|
||||
|
||||
static uint32_t g_tea_key[4] =
|
||||
{
|
||||
CONFIG_FSUTILS_PASSWD_KEY1,
|
||||
CONFIG_FSUTILS_PASSWD_KEY2,
|
||||
CONFIG_FSUTILS_PASSWD_KEY3,
|
||||
CONFIG_FSUTILS_PASSWD_KEY4
|
||||
};
|
||||
#define PASSWD_MIN_LENGTH 8
|
||||
|
||||
static const char g_password_specials[] =
|
||||
"!@#$%^&*()_+-=[]{}|;:,.<>?";
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int validate_password_complexity(FAR const char *password)
|
||||
{
|
||||
FAR const char *p;
|
||||
size_t passlen;
|
||||
int has_upper = 0;
|
||||
int has_lower = 0;
|
||||
int has_digit = 0;
|
||||
int has_special = 0;
|
||||
|
||||
passlen = strlen(password);
|
||||
if (passlen < PASSWD_MIN_LENGTH)
|
||||
{
|
||||
_err("ERROR: password must be at least %d characters\n",
|
||||
PASSWD_MIN_LENGTH);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (passlen > MAX_PASSWORD)
|
||||
{
|
||||
_err("ERROR: password must be at most %d characters\n", MAX_PASSWORD);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (p = password; *p != '\0'; p++)
|
||||
{
|
||||
if (isupper((unsigned char)*p))
|
||||
{
|
||||
has_upper = 1;
|
||||
}
|
||||
else if (islower((unsigned char)*p))
|
||||
{
|
||||
has_lower = 1;
|
||||
}
|
||||
else if (isdigit((unsigned char)*p))
|
||||
{
|
||||
has_digit = 1;
|
||||
}
|
||||
else if (strchr(g_password_specials, *p) != NULL)
|
||||
{
|
||||
has_special = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_upper)
|
||||
{
|
||||
_err("ERROR: password must contain at least one uppercase "
|
||||
"letter (A-Z)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!has_lower)
|
||||
{
|
||||
_err("ERROR: password must contain at least one lowercase "
|
||||
"letter (a-z)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!has_digit)
|
||||
{
|
||||
_err("ERROR: password must contain at least one digit (0-9)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!has_special)
|
||||
{
|
||||
_err("ERROR: password must contain at least one special "
|
||||
"character (!@#$%%^&*()_+-=[]{}|;:,.<>?)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: passwd_base64
|
||||
* Name: passwd_fill_random
|
||||
*
|
||||
* Description:
|
||||
* Encode a 5 bit value as a base64 character.
|
||||
*
|
||||
* Input Parameters:
|
||||
* binary - 5 bit value
|
||||
*
|
||||
* Returned Value:
|
||||
* The ASCII base64 character. Must not return the field delimiter ':'
|
||||
* Fill a buffer with random bytes using getrandom() or /dev/urandom.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static char passwd_base64(uint8_t binary)
|
||||
static int passwd_fill_random(FAR uint8_t *buf, size_t len)
|
||||
{
|
||||
/* 0-26 -> 'A'-'Z' */
|
||||
ssize_t nread;
|
||||
int fd;
|
||||
|
||||
binary &= 63;
|
||||
if (binary < 26)
|
||||
nread = getrandom(buf, len, 0);
|
||||
if (nread == (ssize_t)len)
|
||||
{
|
||||
return 'A' + binary;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* 26-51 -> 'a'-'z' */
|
||||
|
||||
binary -= 26;
|
||||
if (binary < 26)
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return 'a' + binary;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* 52->61 -> '0'-'9' */
|
||||
nread = read(fd, buf, len);
|
||||
close(fd);
|
||||
|
||||
binary -= 26;
|
||||
if (binary < 10)
|
||||
if (nread != (ssize_t)len)
|
||||
{
|
||||
return '0' + binary;
|
||||
return nread < 0 ? -errno : -EIO;
|
||||
}
|
||||
|
||||
/* 62 -> '+' */
|
||||
|
||||
binary -= 10;
|
||||
if (binary == 0)
|
||||
{
|
||||
return '+';
|
||||
}
|
||||
|
||||
/* 63 -> '/' */
|
||||
|
||||
return '/';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -114,109 +174,66 @@ static char passwd_base64(uint8_t binary)
|
|||
* Name: passwd_encrypt
|
||||
*
|
||||
* Description:
|
||||
* Encrypt a password. Currently uses the Tiny Encryption Algorithm.
|
||||
*
|
||||
* Input Parameters:
|
||||
* password -- The password string to be encrypted
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
||||
* failure.
|
||||
* Hash a password with PBKDF2-HMAC-SHA256 and encode as modular crypt
|
||||
* format: $pbkdf2-sha256$<iter>$<base64url-salt>$<base64url-hash>
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int passwd_encrypt(FAR const char *password,
|
||||
char encrypted[MAX_ENCRYPTED + 1])
|
||||
{
|
||||
union
|
||||
{
|
||||
char b[8];
|
||||
uint16_t h[4];
|
||||
uint32_t l[2];
|
||||
} value;
|
||||
uint8_t salt[PASSWD_SALT_BYTES];
|
||||
uint8_t hash[PASSWD_HASH_BYTES];
|
||||
char salt_b64[32];
|
||||
char hash_b64[48];
|
||||
size_t passlen;
|
||||
int ret;
|
||||
|
||||
FAR const char *src;
|
||||
FAR char *bptr;
|
||||
FAR char *dest;
|
||||
uint32_t tmp;
|
||||
uint8_t remainder;
|
||||
int remaining;
|
||||
int gulpsize;
|
||||
int nbits;
|
||||
int i;
|
||||
ret = validate_password_complexity(password);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* How long is the password? */
|
||||
passlen = strlen(password);
|
||||
|
||||
remaining = strlen(password);
|
||||
if (remaining > MAX_PASSWORD)
|
||||
ret = passwd_fill_random(salt, sizeof(salt));
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = passwd_pbkdf2_hmac_sha256((FAR const uint8_t *)password, passlen,
|
||||
salt, sizeof(salt),
|
||||
CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS,
|
||||
hash, sizeof(hash));
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = base64url_encode(salt, sizeof(salt), salt_b64,
|
||||
sizeof(salt_b64));
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = base64url_encode(hash, sizeof(hash), hash_b64,
|
||||
sizeof(hash_b64));
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snprintf(encrypted, MAX_ENCRYPTED + 1,
|
||||
PASSWD_MCF_PREFIX "%u$%s$%s",
|
||||
CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS,
|
||||
salt_b64, hash_b64);
|
||||
if (ret < 0 || (size_t)ret > MAX_ENCRYPTED)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* Convert the password in 8-byte TEA cycles */
|
||||
|
||||
src = password;
|
||||
dest = encrypted;
|
||||
*dest = '\0';
|
||||
|
||||
remainder = 0;
|
||||
nbits = 0;
|
||||
|
||||
for (; remaining > 0; remaining -= gulpsize)
|
||||
{
|
||||
/* Copy bytes */
|
||||
|
||||
gulpsize = sizeof(value.b);
|
||||
if (gulpsize > remaining)
|
||||
{
|
||||
gulpsize = remaining;
|
||||
}
|
||||
|
||||
bptr = value.b;
|
||||
for (i = 0; i < gulpsize; i++)
|
||||
{
|
||||
*bptr++ = *src++;
|
||||
}
|
||||
|
||||
/* Pad with spaces if necessary */
|
||||
|
||||
for (; i < sizeof(value.b); i++)
|
||||
{
|
||||
*bptr++ = ' ';
|
||||
}
|
||||
|
||||
/* Perform the conversion for this cycle */
|
||||
|
||||
tea_encrypt(value.l, g_tea_key);
|
||||
|
||||
/* Generate the base64 output string from this cycle */
|
||||
|
||||
tmp = remainder;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
tmp = (uint32_t)value.h[i] << nbits | tmp;
|
||||
nbits += 16;
|
||||
|
||||
while (nbits >= 6)
|
||||
{
|
||||
*dest++ = passwd_base64((uint8_t)(tmp & 0x3f));
|
||||
tmp >>= 6;
|
||||
nbits -= 6;
|
||||
}
|
||||
}
|
||||
|
||||
remainder = (uint8_t)tmp;
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
/* Handle any remainder */
|
||||
|
||||
if (nbits > 0)
|
||||
{
|
||||
*dest++ = passwd_base64(remainder);
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
|
|||
114
fsutils/passwd/passwd_pbkdf2.c
Normal file
114
fsutils/passwd/passwd_pbkdf2.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/****************************************************************************
|
||||
* apps/fsutils/passwd/passwd_pbkdf2.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <crypto/cryptodev.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "passwd_pbkdf2.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int passwd_pbkdf2_hmac_sha256(FAR const uint8_t *pass, size_t passlen,
|
||||
FAR const uint8_t *salt, size_t saltlen,
|
||||
uint32_t iterations,
|
||||
FAR uint8_t *out, size_t outlen)
|
||||
{
|
||||
struct session_op session;
|
||||
struct crypt_op cryp;
|
||||
int cryptodev_fd = -1;
|
||||
int fd = -1;
|
||||
int ret = 0;
|
||||
|
||||
if (pass == NULL || salt == NULL || out == NULL || passlen == 0 ||
|
||||
saltlen == 0 || iterations == 0 || outlen == 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open("/dev/crypto", O_RDWR, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (ioctl(fd, CRIOGET, &cryptodev_fd) < 0)
|
||||
{
|
||||
ret = -errno;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
memset(&session, 0, sizeof(session));
|
||||
session.cipher = 0;
|
||||
session.mac = CRYPTO_PBKDF2_HMAC_SHA256;
|
||||
session.mackey = (caddr_t)pass;
|
||||
session.mackeylen = passlen;
|
||||
|
||||
if (ioctl(cryptodev_fd, CIOCGSESSION, &session) < 0)
|
||||
{
|
||||
ret = -errno;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
memset(&cryp, 0, sizeof(cryp));
|
||||
cryp.ses = session.ses;
|
||||
cryp.op = COP_ENCRYPT;
|
||||
cryp.src = (caddr_t)salt;
|
||||
cryp.len = saltlen;
|
||||
cryp.mac = (caddr_t)out;
|
||||
cryp.iterations = iterations;
|
||||
cryp.olen = outlen;
|
||||
|
||||
if (ioctl(cryptodev_fd, CIOCCRYPT, &cryp) < 0)
|
||||
{
|
||||
ret = -errno;
|
||||
goto errout_with_session;
|
||||
}
|
||||
|
||||
errout_with_session:
|
||||
ioctl(cryptodev_fd, CIOCFSESSION, &session.ses);
|
||||
|
||||
errout:
|
||||
if (cryptodev_fd >= 0)
|
||||
{
|
||||
close(cryptodev_fd);
|
||||
}
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
44
fsutils/passwd/passwd_pbkdf2.h
Normal file
44
fsutils/passwd/passwd_pbkdf2.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/****************************************************************************
|
||||
* apps/fsutils/passwd/passwd_pbkdf2.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H
|
||||
#define __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/compiler.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
int passwd_pbkdf2_hmac_sha256(FAR const uint8_t *pass, size_t passlen,
|
||||
FAR const uint8_t *salt, size_t saltlen,
|
||||
uint32_t iterations,
|
||||
FAR uint8_t *out, size_t outlen);
|
||||
|
||||
#endif /* __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H */
|
||||
|
|
@ -24,11 +24,105 @@
|
|||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <semaphore.h>
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <netutils/base64.h>
|
||||
#include <fsutils/passwd.h>
|
||||
|
||||
#include "fsutils/passwd.h"
|
||||
#include "passwd.h"
|
||||
#include "passwd_pbkdf2.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: passwd_verify_hash
|
||||
*
|
||||
* Description:
|
||||
* Verify a password against a stored PBKDF2-SHA256 modular crypt hash.
|
||||
* Rejects any hash not in $pbkdf2-sha256$ format.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on match, -1 on mismatch or parse error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int passwd_verify_hash(FAR const char *stored,
|
||||
FAR const char *password)
|
||||
{
|
||||
FAR const char *p;
|
||||
FAR const char *salt_b64;
|
||||
FAR const char *hash_b64;
|
||||
char *endptr;
|
||||
uint8_t salt[PASSWD_SALT_BYTES];
|
||||
uint8_t expected[PASSWD_HASH_BYTES];
|
||||
uint8_t actual[PASSWD_HASH_BYTES];
|
||||
size_t saltlen;
|
||||
size_t hashlen;
|
||||
size_t passlen;
|
||||
unsigned long iterations;
|
||||
int ret;
|
||||
|
||||
if (strncmp(stored, PASSWD_MCF_PREFIX, strlen(PASSWD_MCF_PREFIX)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = stored + strlen(PASSWD_MCF_PREFIX);
|
||||
iterations = strtoul(p, &endptr, 10);
|
||||
if (endptr == p || *endptr != '$' || iterations < 1 ||
|
||||
iterations > 200000)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
salt_b64 = endptr + 1;
|
||||
hash_b64 = strchr(salt_b64, '$');
|
||||
if (hash_b64 == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = base64url_decode(salt_b64, salt, sizeof(salt), &saltlen);
|
||||
if (ret < 0 || saltlen == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = base64url_decode(hash_b64 + 1, expected, sizeof(expected),
|
||||
&hashlen);
|
||||
if (ret < 0 || hashlen != PASSWD_HASH_BYTES)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
passlen = strlen(password);
|
||||
if (passlen == 0 || passlen > MAX_PASSWORD)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = passwd_pbkdf2_hmac_sha256((FAR const uint8_t *)password, passlen,
|
||||
salt, saltlen, (uint32_t)iterations,
|
||||
actual, sizeof(actual));
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timingsafe_bcmp(actual, expected, sizeof(expected)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
|
|
@ -39,53 +133,33 @@
|
|||
*
|
||||
* Description:
|
||||
* Return true if the username exists in the /etc/passwd file and if the
|
||||
* password matches the user password in that failed.
|
||||
*
|
||||
* Input Parameters:
|
||||
* password matches the user password in that file.
|
||||
*
|
||||
* Returned Value:
|
||||
* One (1) is returned on success match, Zero (OK) is returned on an
|
||||
* unsuccessful match; a negated errno value is returned on any other
|
||||
* failure.
|
||||
* Zero (0) is returned on a successful match, -1 on mismatch or invalid
|
||||
* hash format; a negated errno value is returned on other failures.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int passwd_verify(FAR const char *username, FAR const char *password)
|
||||
{
|
||||
struct passwd_s passwd;
|
||||
char encrypted[MAX_ENCRYPTED + 1];
|
||||
PASSWD_SEM_DECL(sem);
|
||||
int ret;
|
||||
|
||||
/* Get exclusive access to the /etc/passwd file */
|
||||
|
||||
ret = passwd_lock(&sem);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Verify that the username exists in the /etc/passwd file */
|
||||
|
||||
ret = passwd_find(username, &passwd);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* The username does not exist in the /etc/passwd file */
|
||||
|
||||
goto errout_with_lock;
|
||||
}
|
||||
|
||||
/* Encrypt the provided password */
|
||||
|
||||
ret = passwd_encrypt(password, encrypted);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_lock;
|
||||
}
|
||||
|
||||
/* Compare the encrypted passwords */
|
||||
|
||||
ret = (strcmp(passwd.encrypted, encrypted) == 0) ? 1 : 0;
|
||||
ret = passwd_verify_hash(passwd.encrypted, password);
|
||||
|
||||
errout_with_lock:
|
||||
passwd_unlock(sem);
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@
|
|||
|
||||
/* passwd_verify() return value tests */
|
||||
|
||||
#define PASSWORD_VERIFY_MATCH(ret) (ret == 1)
|
||||
#define PASSWORD_VERIFY_NOMATCH(ret) (ret == 0)
|
||||
#define PASSWORD_VERIFY_ERROR(ret) (ret < 0)
|
||||
#define PASSWORD_VERIFY_MATCH(ret) ((ret) == 0)
|
||||
#define PASSWORD_VERIFY_NOMATCH(ret) ((ret) == -1)
|
||||
#define PASSWORD_VERIFY_ERROR(ret) ((ret) < -1)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
|
|
@ -115,9 +115,8 @@ int passwd_update(FAR const char *username, FAR const char *password);
|
|||
* password - The password to be verified
|
||||
*
|
||||
* Returned Value:
|
||||
* One (1) is returned on success match, Zero (OK) is returned on an
|
||||
* unsuccessful match; a negated errno value is returned on any other
|
||||
* failure.
|
||||
* Zero (0) is returned on a successful match, -1 on mismatch or invalid
|
||||
* hash format; a negated errno value is returned on other failures.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -30,17 +30,17 @@
|
|||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -73,6 +73,10 @@ FAR void *base64w_encode(FAR const void *src, size_t len, FAR void *dst,
|
|||
FAR size_t *out_len);
|
||||
FAR void *base64w_decode(FAR const void *src, size_t len, FAR void *dst,
|
||||
FAR size_t *out_len);
|
||||
int base64url_encode(FAR const void *src, size_t len, FAR char *dst,
|
||||
size_t dstlen);
|
||||
int base64url_decode(FAR const char *src, FAR void *dst, size_t dstmax,
|
||||
FAR size_t *out_len);
|
||||
#endif /* CONFIG_CODECS_BASE64 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ config CODECS_BASE64
|
|||
default n
|
||||
---help---
|
||||
Enables support for the following interfaces: base64_encode(),
|
||||
base64_decode(), base64w_encode(), and base64w_decode(),
|
||||
base64_decode(), base64w_encode(), base64w_decode(),
|
||||
base64url_encode(), and base64url_decode(),
|
||||
|
||||
Contributed NuttX by Darcy Gong.
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "netutils/base64.h"
|
||||
|
|
@ -317,4 +318,150 @@ FAR void *base64w_decode(FAR const void *src, size_t len, FAR void *dst,
|
|||
return _base64_decode(src, len, dst, out_len, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: base64url_val
|
||||
****************************************************************************/
|
||||
|
||||
static int base64url_val(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
return c - 'A';
|
||||
}
|
||||
|
||||
if (c >= 'a' && c <= 'z')
|
||||
{
|
||||
return c - 'a' + 26;
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return c - '0' + 52;
|
||||
}
|
||||
|
||||
if (c == '-')
|
||||
{
|
||||
return 62;
|
||||
}
|
||||
|
||||
if (c == '_')
|
||||
{
|
||||
return 63;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: base64url_encode
|
||||
*
|
||||
* Description:
|
||||
* Encode binary data as unpadded base64url (RFC 4648 section 5).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int base64url_encode(FAR const void *src, size_t len, FAR char *dst,
|
||||
size_t dstlen)
|
||||
{
|
||||
FAR const uint8_t *in = src;
|
||||
uint32_t acc = 0;
|
||||
size_t i;
|
||||
size_t o = 0;
|
||||
int bits = 0;
|
||||
|
||||
static const char g_base64url[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
acc = (acc << 8) | in[i];
|
||||
bits += 8;
|
||||
|
||||
while (bits >= 6)
|
||||
{
|
||||
if (o + 1 >= dstlen)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
bits -= 6;
|
||||
dst[o++] = g_base64url[(acc >> bits) & 0x3f];
|
||||
}
|
||||
}
|
||||
|
||||
if (bits > 0)
|
||||
{
|
||||
if (o + 1 >= dstlen)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
dst[o++] = g_base64url[(acc << (6 - bits)) & 0x3f];
|
||||
}
|
||||
|
||||
if (o >= dstlen)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
dst[o] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: base64url_decode
|
||||
*
|
||||
* Description:
|
||||
* Decode unpadded base64url (RFC 4648 section 5).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int base64url_decode(FAR const char *src, FAR void *dst, size_t dstmax,
|
||||
FAR size_t *out_len)
|
||||
{
|
||||
FAR uint8_t *out = dst;
|
||||
uint32_t acc = 0;
|
||||
size_t o = 0;
|
||||
int bits = 0;
|
||||
int v;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
while (*src != '\0')
|
||||
{
|
||||
if (*src == '$' || *src == ':')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v = base64url_val(*src++);
|
||||
if (v < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
acc = (acc << 6) | (uint32_t)v;
|
||||
bits += 6;
|
||||
|
||||
if (bits >= 8)
|
||||
{
|
||||
bits -= 8;
|
||||
if (o >= dstmax)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
out[o++] = (uint8_t)((acc >> bits) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
if (bits >= 6)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*out_len = o;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CODECS_BASE64 */
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ menuconfig NETUTILS_DROPBEAR
|
|||
depends on DEV_URANDOM
|
||||
depends on LIBC_NETDB
|
||||
depends on LIBC_GAISTRERROR
|
||||
select CRYPTO
|
||||
select CRYPTO_RANDOM_POOL
|
||||
depends on CRYPTO
|
||||
depends on CRYPTO_RANDOM_POOL
|
||||
---help---
|
||||
Enable a minimal Dropbear SSH server port for NuttX. This initial
|
||||
port is based on the ESP-IDF MCU test port and provides a single
|
||||
|
|
|
|||
|
|
@ -1287,17 +1287,20 @@ endchoice # Verification method
|
|||
|
||||
config NSH_LOGIN_USERNAME
|
||||
string "Login username"
|
||||
default "admin"
|
||||
default "root"
|
||||
depends on !NSH_LOGIN_PASSWD
|
||||
---help---
|
||||
Login user name. Default: "admin"
|
||||
Login user name. Default: "root"
|
||||
|
||||
config NSH_LOGIN_PASSWORD
|
||||
string "Login password"
|
||||
default "Administrator"
|
||||
depends on !NSH_LOGIN_PASSWD
|
||||
depends on NSH_LOGIN_FIXED
|
||||
---help---
|
||||
Login password: Default: "Administrator"
|
||||
The plaintext login password used when fixed username/password
|
||||
verification is selected (CONFIG_NSH_LOGIN_FIXED). There is no
|
||||
default; set CONFIG_NSH_LOGIN_PASSWORD in the board defconfig,
|
||||
via menuconfig, or enter it when prompted during an interactive
|
||||
build.
|
||||
|
||||
config NSH_LOGIN_FAILDELAY
|
||||
int "Login failure delay"
|
||||
|
|
|
|||
11
nshlib/nsh.h
11
nshlib/nsh.h
|
|
@ -266,8 +266,9 @@
|
|||
* If CONFIG_NSH_TELNET_LOGIN is defined, then these additional
|
||||
* options may be specified:
|
||||
*
|
||||
* CONFIG_NSH_LOGIN_USERNAME - Login user name. Default: "admin"
|
||||
* CONFIG_NSH_LOGIN_PASSWORD - Login password: Default: "Administrator"
|
||||
* CONFIG_NSH_LOGIN_USERNAME - Login user name. Default: "root"
|
||||
* CONFIG_NSH_LOGIN_PASSWORD - Login password (required when using fixed
|
||||
* credentials; no default is provided).
|
||||
* CONFIG_NSH_LOGIN_FAILCOUNT - Number of login retry attempts.
|
||||
* Default 3.
|
||||
*/
|
||||
|
|
@ -275,11 +276,7 @@
|
|||
#ifdef CONFIG_NSH_TELNET_LOGIN
|
||||
|
||||
# ifndef CONFIG_NSH_LOGIN_USERNAME
|
||||
# define CONFIG_NSH_LOGIN_USERNAME "admin"
|
||||
# endif
|
||||
|
||||
# ifndef CONFIG_NSH_LOGIN_PASSWORD
|
||||
# define CONFIG_NSH_LOGIN_PASSWORD "nuttx"
|
||||
# define CONFIG_NSH_LOGIN_USERNAME "root"
|
||||
# endif
|
||||
|
||||
# ifndef CONFIG_NSH_LOGIN_FAILCOUNT
|
||||
|
|
|
|||
24
testing/crypto/CMakeLists.txt
Normal file
24
testing/crypto/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# ##############################################################################
|
||||
# apps/testing/crypto/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
nuttx_add_subdirectory()
|
||||
nuttx_generate_kconfig(MENUDESC "crypto")
|
||||
23
testing/crypto/Make.defs
Normal file
23
testing/crypto/Make.defs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
############################################################################
|
||||
# apps/testing/crypto/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(wildcard $(APPDIR)/testing/crypto/*/Make.defs)
|
||||
37
testing/crypto/passwd/CMakeLists.txt
Normal file
37
testing/crypto/passwd/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# ##############################################################################
|
||||
# apps/testing/crypto/passwd/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_TESTING_PBKDF2)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
pbkdf2_test
|
||||
PRIORITY
|
||||
${CONFIG_TESTING_PBKDF2_PRIORITY}
|
||||
STACKSIZE
|
||||
${CONFIG_TESTING_PBKDF2_STACKSIZE}
|
||||
MODULE
|
||||
${CONFIG_TESTING_PBKDF2}
|
||||
SRCS
|
||||
pbkdf2_test.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../fsutils/passwd)
|
||||
endif()
|
||||
34
testing/crypto/passwd/Kconfig
Normal file
34
testing/crypto/passwd/Kconfig
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config TESTING_PBKDF2
|
||||
tristate "PBKDF2 and passwd hash test"
|
||||
default n
|
||||
depends on FSUTILS_PASSWD
|
||||
---help---
|
||||
Enable the PBKDF2-HMAC-SHA256 unit test
|
||||
(apps/testing/crypto/passwd). Always runs RFC 6070 SHA-256
|
||||
vectors. The passwd_encrypt / passwd_verify round-trip runs only
|
||||
when FSUTILS_PASSWD_READONLY is disabled and DEV_URANDOM is
|
||||
enabled; otherwise it is skipped with an explanatory message.
|
||||
|
||||
if TESTING_PBKDF2
|
||||
|
||||
config TESTING_PBKDF2_PRIORITY
|
||||
int "pbkdf2_test task priority"
|
||||
default 100
|
||||
|
||||
config TESTING_PBKDF2_STACKSIZE
|
||||
int "pbkdf2_test stack size"
|
||||
default DEFAULT_TASK_STACKSIZE
|
||||
|
||||
config TESTING_PBKDF2_SLOW_VECTOR
|
||||
bool "Run RFC 6070 vector with 16777216 iterations"
|
||||
default n
|
||||
---help---
|
||||
Include RFC 6070 test vector #4 (16,777,216 iterations). This
|
||||
takes a long time on embedded targets; enable only for manual runs.
|
||||
|
||||
endif
|
||||
25
testing/crypto/passwd/Make.defs
Normal file
25
testing/crypto/passwd/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/testing/crypto/passwd/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_TESTING_PBKDF2),)
|
||||
CONFIGURED_APPS += $(APPDIR)/testing/crypto/passwd
|
||||
endif
|
||||
34
testing/crypto/passwd/Makefile
Normal file
34
testing/crypto/passwd/Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
############################################################################
|
||||
# apps/testing/crypto/passwd/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
PRIORITY = $(CONFIG_TESTING_PBKDF2_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_TESTING_PBKDF2_STACKSIZE)
|
||||
MODULE = $(CONFIG_TESTING_PBKDF2)
|
||||
|
||||
MAINSRC = pbkdf2_test.c
|
||||
PROGNAME = pbkdf2_test
|
||||
|
||||
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/fsutils/passwd
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
287
testing/crypto/passwd/pbkdf2_test.c
Normal file
287
testing/crypto/passwd/pbkdf2_test.c
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/****************************************************************************
|
||||
* apps/testing/crypto/passwd/pbkdf2_test.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fsutils/passwd.h>
|
||||
|
||||
#include "passwd.h"
|
||||
#include "passwd_pbkdf2.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define TEST_USERNAME "testuser"
|
||||
#define TEST_PASSWORD "MySecret1!"
|
||||
#define WRONG_PASSWORD "WrongPass"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct pbkdf2_vector_s
|
||||
{
|
||||
FAR const char *password;
|
||||
size_t passwordlen;
|
||||
FAR const char *salt;
|
||||
size_t saltlen;
|
||||
uint32_t iterations;
|
||||
size_t dklen;
|
||||
FAR const uint8_t *expected;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/* PBKDF2-HMAC-SHA256 test vectors from RFC 6070 (appendix B). */
|
||||
|
||||
static const uint8_t g_vector1[] =
|
||||
{
|
||||
0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c,
|
||||
0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37,
|
||||
0xa8, 0x65, 0x48, 0xc9
|
||||
};
|
||||
|
||||
static const uint8_t g_vector2[] =
|
||||
{
|
||||
0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3,
|
||||
0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0,
|
||||
0x2a, 0x30, 0x3f, 0x8e
|
||||
};
|
||||
|
||||
static const uint8_t g_vector3[] =
|
||||
{
|
||||
0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41,
|
||||
0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d,
|
||||
0x96, 0x28, 0x93, 0xa0
|
||||
};
|
||||
|
||||
static const uint8_t g_vector4[] =
|
||||
{
|
||||
0xcf, 0x81, 0xc6, 0x6f, 0xe8, 0xcf, 0xc0, 0x4d,
|
||||
0x1f, 0x31, 0xec, 0xb6, 0x5d, 0xab, 0x40, 0x89,
|
||||
0xf7, 0xf1, 0x79, 0xe8
|
||||
};
|
||||
|
||||
static const uint8_t g_vector5[] =
|
||||
{
|
||||
0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f,
|
||||
0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf,
|
||||
0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18,
|
||||
0x1c
|
||||
};
|
||||
|
||||
static const uint8_t g_vector6[] =
|
||||
{
|
||||
0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89,
|
||||
0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87
|
||||
};
|
||||
|
||||
static const struct pbkdf2_vector_s g_vectors[] =
|
||||
{
|
||||
{
|
||||
"password", 8,
|
||||
"salt", 4,
|
||||
1, 20,
|
||||
g_vector1
|
||||
},
|
||||
{
|
||||
"password", 8,
|
||||
"salt", 4,
|
||||
2, 20,
|
||||
g_vector2
|
||||
},
|
||||
{
|
||||
"password", 8,
|
||||
"salt", 4,
|
||||
4096, 20,
|
||||
g_vector3
|
||||
},
|
||||
{
|
||||
"password", 8,
|
||||
"salt", 4,
|
||||
16777216, 20,
|
||||
g_vector4
|
||||
},
|
||||
{
|
||||
"passwordPASSWORDpassword", 24,
|
||||
"saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
|
||||
4096, 25,
|
||||
g_vector5
|
||||
},
|
||||
{
|
||||
"pass\0word", 9,
|
||||
"sa\0lt", 5,
|
||||
4096, 16,
|
||||
g_vector6
|
||||
},
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int test_pbkdf2_vectors(void)
|
||||
{
|
||||
FAR const struct pbkdf2_vector_s *vec;
|
||||
uint8_t output[32];
|
||||
int failures = 0;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < (int)(sizeof(g_vectors) / sizeof(g_vectors[0])); i++)
|
||||
{
|
||||
vec = &g_vectors[i];
|
||||
|
||||
#ifndef CONFIG_TESTING_PBKDF2_SLOW_VECTOR
|
||||
if (vec->iterations > 100000)
|
||||
{
|
||||
printf("pbkdf2_test: skipping slow vector %d (enable "
|
||||
"TESTING_PBKDF2_SLOW_VECTOR)\n", i);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = passwd_pbkdf2_hmac_sha256((FAR const uint8_t *)vec->password,
|
||||
vec->passwordlen,
|
||||
(FAR const uint8_t *)vec->salt,
|
||||
vec->saltlen,
|
||||
vec->iterations,
|
||||
output, vec->dklen);
|
||||
if (ret != 0)
|
||||
{
|
||||
printf("pbkdf2_test: vector %d pbkdf2 failed: %d\n", i, ret);
|
||||
failures++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(output, vec->expected, vec->dklen) != 0)
|
||||
{
|
||||
printf("pbkdf2_test: vector %d output mismatch\n", i);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
printf("pbkdf2_test: RFC 6070 SHA-256 vectors OK\n");
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
static int test_passwd_roundtrip(void)
|
||||
{
|
||||
#if defined(CONFIG_FSUTILS_PASSWD_READONLY)
|
||||
printf("pbkdf2_test: skipping passwd round-trip "
|
||||
"(FSUTILS_PASSWD_READONLY)\n");
|
||||
return 0;
|
||||
#elif !defined(CONFIG_DEV_URANDOM)
|
||||
printf("pbkdf2_test: skipping passwd round-trip "
|
||||
"(DEV_URANDOM)\n");
|
||||
return 0;
|
||||
#else
|
||||
FILE *stream;
|
||||
char encrypted[MAX_ENCRYPTED + 1];
|
||||
int ret;
|
||||
|
||||
unlink(CONFIG_FSUTILS_PASSWD_PATH);
|
||||
|
||||
ret = passwd_encrypt(TEST_PASSWORD, encrypted);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("pbkdf2_test: passwd_encrypt failed: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
stream = fopen(CONFIG_FSUTILS_PASSWD_PATH, "w");
|
||||
if (stream == NULL)
|
||||
{
|
||||
printf("pbkdf2_test: cannot write %s: %d\n",
|
||||
CONFIG_FSUTILS_PASSWD_PATH, errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fprintf(stream, "%s:%s:0:0:/\n", TEST_USERNAME, encrypted) < 0)
|
||||
{
|
||||
printf("pbkdf2_test: fprintf failed: %d\n", errno);
|
||||
fclose(stream);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
|
||||
ret = passwd_verify(TEST_USERNAME, TEST_PASSWORD);
|
||||
if (ret != 0)
|
||||
{
|
||||
printf("pbkdf2_test: passwd_verify match failed: %d (expected 0)\n",
|
||||
ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = passwd_verify(TEST_USERNAME, WRONG_PASSWORD);
|
||||
if (ret != -1)
|
||||
{
|
||||
printf("pbkdf2_test: passwd_verify mismatch failed: %d "
|
||||
"(expected -1)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("pbkdf2_test: passwd round-trip OK\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char *argv[])
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
failures += test_pbkdf2_vectors();
|
||||
failures += test_passwd_roundtrip();
|
||||
|
||||
if (failures != 0)
|
||||
{
|
||||
printf("pbkdf2_test: FAILED (%d)\n", failures);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("pbkdf2_test: PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue