!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:
Abhishek Mishra 2026-06-25 16:55:03 +00:00 committed by Xiang Xiao
parent 01c6a60581
commit 608f13fd4b
22 changed files with 1096 additions and 226 deletions

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View 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;
}

View 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 */

View file

@ -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);

View file

@ -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.
*
****************************************************************************/

View file

@ -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

View file

@ -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.

View file

@ -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 */

View file

@ -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

View file

@ -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"

View file

@ -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

View 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
View 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)

View 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()

View 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

View 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

View 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

View 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;
}