crypt(3): match the Argon2 reference implementation's Base64 exactly

There are too many minor variations regarding padding and exact alphabet
to safely use the implementation in libc or an existing implementation
in libcrypt.
This commit is contained in:
nia 2021-10-12 15:25:39 +00:00
parent da66f63158
commit f7145efd49
4 changed files with 112 additions and 33 deletions

View File

@ -7,7 +7,6 @@
#include <pwd.h>
#include <errno.h>
#include <argon2.h>
#include <resolv.h> /* for b64_pton... */
#include <err.h>
#include "crypt.h"
@ -32,17 +31,84 @@
#define ARGON2_ARGON2D_STR "argon2d"
#define ARGON2_ARGON2ID_STR "argon2id"
/*
* Some macros for constant-time comparisons. These work over values in
* the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
*/
#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
#define GE(x, y) (GT(y, x) ^ 0xFF)
#define LT(x, y) GT(y, x)
#define LE(x, y) GE(y, x)
static unsigned
b64_char_to_byte(int c)
{
unsigned x;
x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
(GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
(GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
(EQ(c, '/') & 63);
return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
}
static const char *
from_base64(void *dst, size_t *dst_len, const char *src)
{
size_t len;
unsigned char *buf;
unsigned acc, acc_len;
buf = (unsigned char *)dst;
len = 0;
acc = 0;
acc_len = 0;
for (;;) {
unsigned d;
d = b64_char_to_byte(*src);
if (d == 0xFF) {
break;
}
src++;
acc = (acc << 6) + d;
acc_len += 6;
if (acc_len >= 8) {
acc_len -= 8;
if ((len++) >= *dst_len) {
return NULL;
}
*buf++ = (acc >> acc_len) & 0xFF;
}
}
/*
* If the input length is equal to 1 modulo 4 (which is
* invalid), then there will remain 6 unprocessed bits;
* otherwise, only 0, 2 or 4 bits are buffered. The buffered
* bits must also all be zero.
*/
if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
return NULL;
}
*dst_len = len;
return src;
}
/* process params to argon2 */
/* we don't force param order as input, */
/* but we do provide the expected order to argon2 api */
static int decode_option(argon2_context * ctx, argon2_type * atype, const char * option)
static int
decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
{
size_t tmp=0;
char * in = 0,*inp;
char * a=0;
char * p=0;
size_t tmp = 0;
char *in = 0, *inp;
char *a = 0;
char *p = 0;
size_t sl;
int error=0;
int error = 0;
in = (char *)strdup(option);
inp = in;
@ -127,7 +193,12 @@ static int decode_option(argon2_context * ctx, argon2_type * atype, const char *
a = strsep(&inp, "$");
b64_pton(a, ctx->salt, ctx->saltlen);
sl = ctx->saltlen;
if (from_base64(ctx->salt, &sl, a) == NULL)
return -1;
ctx->saltlen = sl;
a = strsep(&inp, "$");
@ -151,11 +222,9 @@ __crypt_argon2(const char *pw, const char * salt)
{
/* we use the libargon2 api to generate */
/* return code */
int rc=0;
int rc = 0;
/* output buffer */
char ebuf[32];
/* ptr into argon2 encoded buffer */
char * blkp=0;
/* argon2 variable, default to id */
argon2_type atype = Argon2_id;
/* default to current argon2 version */
@ -184,7 +253,7 @@ __crypt_argon2(const char *pw, const char * salt)
ctx.salt = (uint8_t *)saltbuf;
ctx.saltlen = sizeof(saltbuf);
ctx.pwd= (uint8_t *)pwdbuf;
ctx.pwd = (uint8_t *)pwdbuf;
ctx.pwdlen = sizeof(pwdbuf);
/* decode salt string to argon2 params */
@ -197,8 +266,9 @@ __crypt_argon2(const char *pw, const char * salt)
}
rc = argon2_hash(ctx.t_cost, ctx.m_cost,
ctx.threads, pw, strlen(pw), ctx.salt, strlen((char*)ctx.salt),
ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf), atype, ctx.version);
ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
atype, ctx.version);
if (rc != ARGON2_OK) {
fprintf(stderr, "argon2: failed: %s\n",
@ -206,14 +276,7 @@ __crypt_argon2(const char *pw, const char * salt)
return 0;
}
/* get encoded passwd */
if ((blkp = strrchr(encodebuf, '$')) == NULL) {
return 0;
}
/* skip over '$' */
blkp++;
puts(encodebuf);
memcpy(rbuf, encodebuf, sizeof(encodebuf));
/* clear buffers */

View File

@ -1,5 +1,5 @@
/*
* $NetBSD: crypt.h,v 1.6 2021/10/12 13:24:00 nia Exp $
* $NetBSD: crypt.h,v 1.7 2021/10/12 15:25:39 nia Exp $
*/
#define crypt_private __attribute__((__visibility__("hidden")))
@ -10,7 +10,6 @@ char *__crypt_sha1(const char *pw, const char *salt);
unsigned int __crypt_sha1_iterations (unsigned int hint);
void __hmac_sha1(const unsigned char *, size_t, const unsigned char *, size_t,
unsigned char *);
void __crypt_to64(char *s, u_int32_t v, int n);
#ifdef HAVE_ARGON2
char *__crypt_argon2(const char *pw, const char *salt);
@ -26,6 +25,8 @@ int __gensalt_md5(char *salt, size_t saltsiz, const char *option);
int __gensalt_sha1(char *salt, size_t saltsiz, const char *option);
crypt_private int getnum(const char *, size_t *);
crypt_private void __crypt_to64(char *s, uint32_t v, int n);
crypt_private void __crypt_tobase64(char *s, uint32_t v, int n);
#define SHA1_MAGIC "$sha1$"
#define SHA1_SIZE 20

View File

@ -1,4 +1,4 @@
/* $NetBSD: pw_gensalt.c,v 1.10 2021/10/12 13:24:00 nia Exp $ */
/* $NetBSD: pw_gensalt.c,v 1.11 2021/10/12 15:25:39 nia Exp $ */
/*
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
@ -34,7 +34,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: pw_gensalt.c,v 1.10 2021/10/12 13:24:00 nia Exp $");
__RCSID("$NetBSD: pw_gensalt.c,v 1.11 2021/10/12 15:25:39 nia Exp $");
#endif /* not lint */
#include <sys/syslimits.h>
@ -242,10 +242,10 @@ __gensalt_argon2(char *salt, size_t saltsiz, const char *option,argon2_type atyp
return 0;
}
__crypt_to64(&salt[n], arc4random(), 4);
__crypt_to64(&salt[n + 4], arc4random(), 4);
__crypt_to64(&salt[n + 8], arc4random(), 4);
__crypt_to64(&salt[n + 12], arc4random(), 4);
__crypt_tobase64(&salt[n], arc4random(), 4);
__crypt_tobase64(&salt[n + 4], arc4random(), 4);
__crypt_tobase64(&salt[n + 8], arc4random(), 4);
__crypt_tobase64(&salt[n + 12], arc4random(), 4);
salt[n + 16] = '$';
salt[n + 17] = '\0';

View File

@ -1,4 +1,4 @@
/* $NetBSD: util.c,v 1.2 2021/10/12 13:24:00 nia Exp $ */
/* $NetBSD: util.c,v 1.3 2021/10/12 15:25:39 nia Exp $ */
/*
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
* All rights reserved.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: util.c,v 1.2 2021/10/12 13:24:00 nia Exp $");
__RCSID("$NetBSD: util.c,v 1.3 2021/10/12 15:25:39 nia Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -41,9 +41,14 @@ __RCSID("$NetBSD: util.c,v 1.2 2021/10/12 13:24:00 nia Exp $");
#include "crypt.h"
/* traditional unix "B64" encoding */
static const unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/* standard base64 encoding, used by Argon2 */
static const unsigned char itoabase64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
crypt_private int
getnum(const char *str, size_t *num)
{
@ -68,7 +73,7 @@ getnum(const char *str, size_t *num)
return 0;
}
void
crypt_private void
__crypt_to64(char *s, uint32_t v, int n)
{
@ -77,3 +82,13 @@ __crypt_to64(char *s, uint32_t v, int n)
v >>= 6;
}
}
crypt_private void
__crypt_tobase64(char *s, uint32_t v, int n)
{
while (--n >= 0) {
*s++ = itoabase64[v & 0x3f];
v >>= 6;
}
}