Move OATH development to a branch. OATH will soon disappear entirely
from this repository as Cryb takes over. git-svn-id: svn+ssh://svn.openpam.org/svn/openpam/trunk@907 185d5e19-27fe-0310-9dcf-9bff6b9f3609
This commit is contained in:
parent
3699596d18
commit
c75883564d
49 changed files with 9 additions and 18776 deletions
5
HISTORY
5
HISTORY
|
@ -1,8 +1,3 @@
|
|||
OpenPAM ?????????? 2014-??-??
|
||||
|
||||
- FEATURE: Add a pam_oath module that implements RFC 4226 (HOTP) and
|
||||
RFC 6238 (TOTP).
|
||||
============================================================================
|
||||
OpenPAM Ourouparia 2014-09-12
|
||||
|
||||
- ENHANCE: When executing a chain, require at least one service
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,7 +1,6 @@
|
|||
|
||||
Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
Copyright (c) 2004-2017 Dag-Erling Smørgrav
|
||||
Copyright (c) 2012-2016 The University of Oslo
|
||||
All rights reserved.
|
||||
|
||||
This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
|
5
RELNOTES
5
RELNOTES
|
@ -17,11 +17,6 @@ The distribution consists of the following components:
|
|||
- A test application (pamtest) which can be used to test policies and
|
||||
modules.
|
||||
|
||||
- A library which implements the OATH one-time password algorithms,
|
||||
with complete API documentation.
|
||||
|
||||
- A PAM module which implements OATH-based authentication.
|
||||
|
||||
- Unit tests for limited portions of the libraries.
|
||||
|
||||
Please direct bug reports and inquiries to <des@des.no>.
|
||||
|
|
15
TODO
15
TODO
|
@ -1,18 +1,5 @@
|
|||
Before the next release:
|
||||
|
||||
- Add oath_alloc_secure() which allocates memory using mmap() +
|
||||
mlock() and oath_free_secure() which wipes and frees it.
|
||||
|
||||
- Move key management (locate keyfile, load various key formats,
|
||||
write back after use) into liboath.
|
||||
|
||||
- Implement support for PSKC (RFC 6030) keyfiles.
|
||||
|
||||
- Implement OATH OCRA (RFC 6287) authentication.
|
||||
|
||||
- Determine and document level of compliance with the OATH HOTP /
|
||||
TOTP / OCRA validation server profiles.
|
||||
|
||||
- Rewrite openpam_ttyconv(3).
|
||||
- mostly done, needs review.
|
||||
|
||||
|
@ -20,8 +7,6 @@ Before the next release:
|
|||
documentation are slightly incorrect, OpenPAM's pam_unix(8) is
|
||||
incorrect, all FreeBSD modules are broken)
|
||||
|
||||
- Finish pam_oath(8) and oathkey(1).
|
||||
|
||||
- Add loop detection to openpam_load_chain().
|
||||
|
||||
- Look into the possibility of implementing a version of (or a
|
||||
|
|
|
@ -24,7 +24,6 @@ if [ -z "$CC" -a -z "$CPP" -a -z "$CXX" ] ; then
|
|||
fi
|
||||
|
||||
./configure \
|
||||
--with-oath \
|
||||
--with-doc \
|
||||
--with-pam-unix \
|
||||
--with-pamtest \
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
SUBDIRS = openpam_dump_policy
|
||||
|
||||
if WITH_OATH
|
||||
SUBDIRS += oathkey
|
||||
endif
|
||||
|
||||
if WITH_PAMTEST
|
||||
SUBDIRS += pamtest
|
||||
endif
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
||||
|
||||
bin_PROGRAMS = oathkey
|
||||
oathkey_SOURCES = oathkey.c
|
||||
oathkey_LDADD =
|
||||
if WITH_SYSTEM_LIBOATH
|
||||
oathkey_LDADD += $(SYSTEM_LIBOATH)
|
||||
else
|
||||
oathkey_LDADD += $(top_builddir)/lib/liboath/liboath.la
|
||||
endif
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
oathkey_LDADD += $(SYSTEM_LIBPAM)
|
||||
else
|
||||
oathkey_LDADD += $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
||||
dist_man1_MANS = oathkey.1
|
||||
|
||||
install-exec-hook:
|
||||
chmod u+s $(DESTDIR)$(bindir)/oathkey
|
|
@ -1,141 +0,0 @@
|
|||
.\"-
|
||||
.\" Copyright (c) 2013-2014 The University of Oslo
|
||||
.\" Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. The name of the author may not be used to endorse or promote
|
||||
.\" products derived from this software without specific prior written
|
||||
.\" permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
.\"
|
||||
.\" $Id$
|
||||
.\"
|
||||
.Dd January 9, 2016
|
||||
.Dt OATHKEY 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm oathkey
|
||||
.Nd OATH key management tool
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl hnrvw
|
||||
.Op Fl u Ar user
|
||||
.Op Fl k Ar keyfile
|
||||
.Ar command
|
||||
.Op Ar args
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility creates and manages OATH keys, and can be used to validate an
|
||||
OATH response.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width Fl
|
||||
.It Fl h
|
||||
Print a usage message and exit.
|
||||
.It Fl k Ar keyfile
|
||||
Specify the location of the keyfile on which to operate.
|
||||
The default is
|
||||
.Pa /var/oath/ Ns Ar user Ns Pa .otpauth .
|
||||
.It Fl n
|
||||
When printing codes with the
|
||||
.Cm calc
|
||||
command, print the counter or timestamp along with each code.
|
||||
.It Fl r
|
||||
Disable writeback mode.
|
||||
.It Fl u Ar user
|
||||
Specify the user on which to operate.
|
||||
The default is the current user.
|
||||
Only root may operate on other users.
|
||||
.It Fl v
|
||||
Enable verbose mode.
|
||||
.It Fl w
|
||||
Enable writeback mode (see below).
|
||||
This is the default.
|
||||
.El
|
||||
.Pp
|
||||
The commands are:
|
||||
.Bl -tag -width 6n
|
||||
.It Cm calc Op Ar count
|
||||
Compute and display the current code for the given key.
|
||||
If a count is specified, compute and display
|
||||
.Ar count
|
||||
codes in total, starting with the current code, up to a maximum of
|
||||
1,000 codes.
|
||||
If writeback mode is enabled, the user's keyfile is updated to prevent
|
||||
reuse.
|
||||
.It Cm genkey Ar hotp | totp
|
||||
Generate a new key for the specified OATH mode.
|
||||
If writeback mode is enabled, the user's key is set; otherwise, it is
|
||||
printed to standard output.
|
||||
.It Cm getkey
|
||||
Print the user's key.
|
||||
.It Cm geturi
|
||||
Print the user's key in otpauth URI form.
|
||||
.It Cm resync Ar code1 Ar code2 Op Ar code3
|
||||
Resynchronize an event-mode token that has moved too far ahead of the
|
||||
validation server.
|
||||
The codes provided must be consecutive codes within the
|
||||
resynchronization window.
|
||||
The resynchronization window is 100 if two codes are provided and 1000
|
||||
if three codes are provided.
|
||||
.It Cm setkey Ar uri
|
||||
Set the user's key to the given otpauth URI.
|
||||
.It Cm uri
|
||||
Deprecated synonym for
|
||||
.Cm geturi .
|
||||
.It Cm verify Ar code
|
||||
Verify that the given code is the correct current response for the
|
||||
user's key.
|
||||
If writeback mode is enabled and the response matched, the user's
|
||||
keyfile is updated to prevent reuse.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Cm verify
|
||||
command exits 0 if the code is valid, 1 if it is invalid and >1 if an
|
||||
error occurred.
|
||||
.Pp
|
||||
The
|
||||
.Cm resync
|
||||
command exits 0 if resynchronization was successful, 1 if it failed or
|
||||
the specified key does not support resynchronization, and >1 if an
|
||||
error occurred.
|
||||
.Pp
|
||||
All other commands exit 0 if successful and >1 if an error occurred.
|
||||
.Sh SEE ALSO
|
||||
.Xr oath_hotp 3 ,
|
||||
.Xr oath_key 3 ,
|
||||
.Xr oath_totp 3 ,
|
||||
.Xr pam_oath 8
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
utility and this manual page were written by
|
||||
.An Dag-Erling Sm\(/orgrav Aq des@des.no
|
||||
for the University of Oslo.
|
||||
.Sh BUGS
|
||||
For TOTP keys, the
|
||||
.Cm calc Ar count
|
||||
command will only work correctly for a
|
||||
.Ar count
|
||||
of 1.
|
|
@ -1,543 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2016 The University of Oslo
|
||||
* Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "openpam_asprintf.h"
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/* XXX hardcoded windows */
|
||||
#define HOTP_WINDOW 9
|
||||
#define TOTP_WINDOW 2
|
||||
|
||||
enum { RET_SUCCESS, RET_FAILURE, RET_ERROR, RET_USAGE, RET_UNAUTH };
|
||||
|
||||
static char *user;
|
||||
static char *keyfile;
|
||||
static int verbose;
|
||||
static int readonly;
|
||||
static int numbered;
|
||||
|
||||
static int isroot; /* running as root */
|
||||
static int issameuser; /* real user same as target user */
|
||||
|
||||
/*
|
||||
* Print key in hexadecimal form
|
||||
*/
|
||||
static int
|
||||
oathkey_print_hex(struct oath_key *key)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < key->keylen; ++i)
|
||||
printf("%02x", key->key[i]);
|
||||
printf("\n");
|
||||
return (RET_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print key in otpauth URI form
|
||||
*/
|
||||
static int
|
||||
oathkey_print_uri(struct oath_key *key)
|
||||
{
|
||||
char *keyuri;
|
||||
|
||||
if ((keyuri = oath_key_to_uri(key)) == NULL) {
|
||||
warnx("failed to convert key to otpauth URI");
|
||||
return (RET_ERROR);
|
||||
}
|
||||
printf("%s\n", keyuri);
|
||||
free(keyuri);
|
||||
return (RET_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load key from file
|
||||
*/
|
||||
static int
|
||||
oathkey_load(struct oath_key **key)
|
||||
{
|
||||
|
||||
if (verbose)
|
||||
warnx("loading key from %s", keyfile);
|
||||
if ((*key = oath_key_from_file(keyfile)) == NULL) {
|
||||
warn("%s", keyfile);
|
||||
if (errno == EACCES || errno == EPERM)
|
||||
return (RET_UNAUTH);
|
||||
return (RET_ERROR);
|
||||
}
|
||||
return (RET_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save key to file
|
||||
* XXX liboath should take care of this for us
|
||||
*/
|
||||
static int
|
||||
oathkey_save(struct oath_key *key)
|
||||
{
|
||||
char *keyuri;
|
||||
int fd, len, ret;
|
||||
|
||||
if (verbose)
|
||||
warnx("saving key to %s", keyfile);
|
||||
keyuri = NULL;
|
||||
len = 0;
|
||||
fd = ret = -1;
|
||||
if ((keyuri = oath_key_to_uri(key)) == NULL) {
|
||||
warnx("failed to convert key to otpauth URI");
|
||||
goto done;
|
||||
}
|
||||
len = strlen(keyuri);
|
||||
if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
|
||||
write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
|
||||
warn("%s", keyfile);
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
done:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (keyuri != NULL)
|
||||
free(keyuri);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new key
|
||||
*/
|
||||
static int
|
||||
oathkey_genkey(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
enum oath_mode mode;
|
||||
int ret;
|
||||
|
||||
if (argc != 1)
|
||||
return (RET_USAGE);
|
||||
if ((mode = oath_mode(argv[0])) == om_undef)
|
||||
return (RET_USAGE);
|
||||
if (!isroot && !issameuser)
|
||||
return (RET_UNAUTH);
|
||||
if ((key = oath_key_create(user, mode, oh_undef, NULL, 0)) == NULL)
|
||||
return (RET_ERROR);
|
||||
ret = readonly ? oathkey_print_uri(key) : oathkey_save(key);
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a user's key
|
||||
*/
|
||||
static int
|
||||
oathkey_setkey(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
int ret;
|
||||
|
||||
/* XXX add parameters later */
|
||||
if (argc != 1)
|
||||
return (RET_USAGE);
|
||||
(void)argv;
|
||||
if (!isroot && !issameuser)
|
||||
return (RET_UNAUTH);
|
||||
if ((key = oath_key_from_uri(argv[0])) == NULL)
|
||||
return (RET_ERROR);
|
||||
ret = oathkey_save(key);
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print raw key in hexadecimal
|
||||
*/
|
||||
static int
|
||||
oathkey_getkey(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
int ret;
|
||||
|
||||
if (argc != 0)
|
||||
return (RET_USAGE);
|
||||
(void)argv;
|
||||
if (!isroot && !issameuser)
|
||||
return (RET_UNAUTH);
|
||||
if ((ret = oathkey_load(&key)) != RET_SUCCESS)
|
||||
return (ret);
|
||||
ret = oathkey_print_hex(key);
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the otpauth URI for a key
|
||||
*/
|
||||
static int
|
||||
oathkey_geturi(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
int ret;
|
||||
|
||||
if (argc != 0)
|
||||
return (RET_USAGE);
|
||||
(void)argv;
|
||||
if (!isroot && !issameuser)
|
||||
return (RET_UNAUTH);
|
||||
if ((ret = oathkey_load(&key)) != RET_SUCCESS)
|
||||
return (ret);
|
||||
ret = oathkey_print_uri(key);
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a given response is correct for the given keyfile.
|
||||
*/
|
||||
static int
|
||||
oathkey_verify(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
unsigned long counter;
|
||||
unsigned int response;
|
||||
char *end;
|
||||
int match, ret;
|
||||
|
||||
if (argc < 1)
|
||||
return (RET_USAGE);
|
||||
if ((ret = oathkey_load(&key)) != RET_SUCCESS)
|
||||
return (ret);
|
||||
response = strtoul(*argv, &end, 10);
|
||||
if (end == *argv || *end != '\0')
|
||||
response = UINT_MAX; /* never valid */
|
||||
switch (key->mode) {
|
||||
case om_hotp:
|
||||
counter = key->counter;
|
||||
match = oath_hotp_match(key, response, HOTP_WINDOW);
|
||||
if (verbose && match > 0 && key->counter > counter + 1)
|
||||
warnx("skipped %lu codes", key->counter - counter - 1);
|
||||
break;
|
||||
case om_totp:
|
||||
match = oath_totp_match(key, response, TOTP_WINDOW);
|
||||
break;
|
||||
default:
|
||||
match = -1;
|
||||
}
|
||||
/* oath_*_match() return -1 on error, 0 on failure, 1 on success */
|
||||
if (match < 0) {
|
||||
warnx("OATH error");
|
||||
match = 0;
|
||||
}
|
||||
if (verbose)
|
||||
warnx("response: %u %s", response,
|
||||
match ? "matched" : "did not match");
|
||||
ret = match ? readonly ? RET_SUCCESS : oathkey_save(key) : RET_FAILURE;
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the current code
|
||||
*/
|
||||
static int
|
||||
oathkey_calc(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
unsigned int current;
|
||||
unsigned long i, n;
|
||||
uintmax_t count;
|
||||
char *end;
|
||||
int ret;
|
||||
|
||||
if (argc > 1)
|
||||
return (RET_USAGE);
|
||||
if (argc > 0) {
|
||||
n = strtoul(argv[0], &end, 10);
|
||||
if (end == argv[0] || *end != '\0' || n < 1 || n > 1000)
|
||||
return (RET_USAGE);
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
if ((ret = oathkey_load(&key)) != RET_SUCCESS)
|
||||
return (ret);
|
||||
for (i = 0; i < n; ++i) {
|
||||
switch (key->mode) {
|
||||
case om_hotp:
|
||||
current = oath_hotp_current(key);
|
||||
count = key->counter;
|
||||
break;
|
||||
case om_totp:
|
||||
current = oath_totp_current(key);
|
||||
count = key->lastused * key->timestep;
|
||||
break;
|
||||
default:
|
||||
current = UINT_MAX;
|
||||
count = 0;
|
||||
}
|
||||
if (current == UINT_MAX) {
|
||||
warnx("OATH error");
|
||||
ret = RET_ERROR;
|
||||
break;
|
||||
}
|
||||
if (numbered)
|
||||
printf("%6ju ", count);
|
||||
printf("%.*d\n", (int)key->digits, current);
|
||||
}
|
||||
if (ret == RET_SUCCESS && !readonly)
|
||||
ret = oathkey_save(key);
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resynchronize
|
||||
*/
|
||||
static int
|
||||
oathkey_resync(int argc, char *argv[])
|
||||
{
|
||||
struct oath_key *key;
|
||||
unsigned long counter;
|
||||
unsigned int response[3];
|
||||
char *end;
|
||||
int i, match, n, ret, w;
|
||||
|
||||
if (argc < 2 || argc > 3)
|
||||
return (RET_USAGE);
|
||||
n = argc;
|
||||
for (i = 0, w = 1; i < n; ++i) {
|
||||
response[i] = strtoul(argv[i], &end, 10);
|
||||
if (end == argv[i] || *end != '\0')
|
||||
response[i] = UINT_MAX; /* never valid */
|
||||
w = w * (HOTP_WINDOW + 1);
|
||||
}
|
||||
w -= n;
|
||||
if ((ret = oathkey_load(&key)) != RET_SUCCESS)
|
||||
return (ret);
|
||||
switch (key->mode) {
|
||||
case om_hotp:
|
||||
/* this should be a library function */
|
||||
counter = key->counter;
|
||||
match = 0;
|
||||
while (key->counter < counter + w && match == 0) {
|
||||
match = oath_hotp_match(key, response[0],
|
||||
counter + w - key->counter - 1);
|
||||
if (match <= 0)
|
||||
break;
|
||||
for (i = 1; i < n && match > 0; ++i)
|
||||
match = oath_hotp_match(key, response[i], 0);
|
||||
}
|
||||
if (verbose && match > 0)
|
||||
warnx("skipped %lu codes", key->counter - counter);
|
||||
break;
|
||||
default:
|
||||
match = -1;
|
||||
}
|
||||
if (match < 0) {
|
||||
warnx("OATH error");
|
||||
match = 0;
|
||||
}
|
||||
if (verbose)
|
||||
warnx("resynchronization %s", match ? "succeeded" : "failed");
|
||||
ret = match ? readonly ? RET_SUCCESS : oathkey_save(key) : RET_FAILURE;
|
||||
oath_key_free(key);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print usage string and exit.
|
||||
*/
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: oathkey [-hnrvw] [-u user] [-k keyfile] command\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" calc [count]\n"
|
||||
" Print the next code(s)\n"
|
||||
" genkey hotp | totp\n"
|
||||
" Generate a new key\n"
|
||||
" getkey Print the key in hexadecimal form\n"
|
||||
" geturi Print the key in otpauth URI form\n"
|
||||
" resync code1 code2 [code3]\n"
|
||||
" Resynchronize an HOTP token\n"
|
||||
" setkey Generate a new key\n"
|
||||
" verify code\n"
|
||||
" Verify an HOTP or TOTP code\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct passwd *pw;
|
||||
int opt, ret;
|
||||
char *cmd;
|
||||
|
||||
/*
|
||||
* Parse command-line options
|
||||
*/
|
||||
while ((opt = getopt(argc, argv, "hk:nru:vw")) != -1)
|
||||
switch (opt) {
|
||||
case 'k':
|
||||
keyfile = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
numbered = 1;
|
||||
break;
|
||||
case 'r':
|
||||
readonly = 1;
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
++verbose;
|
||||
break;
|
||||
case 'w':
|
||||
readonly = 0;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc-- < 1)
|
||||
usage();
|
||||
cmd = *argv++;
|
||||
|
||||
/*
|
||||
* Check whether we are (really!) root.
|
||||
*/
|
||||
if (getuid() == 0)
|
||||
isroot = 1;
|
||||
|
||||
/*
|
||||
* If a user was specified on the command line, check whether it
|
||||
* matches our real UID.
|
||||
*/
|
||||
if (user != NULL) {
|
||||
if ((pw = getpwnam(user)) == NULL)
|
||||
errx(1, "no such user");
|
||||
if (getuid() == pw->pw_uid)
|
||||
issameuser = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no user was specified on the command line, look up the user
|
||||
* that corresponds to our real UID.
|
||||
*/
|
||||
if (user == NULL) {
|
||||
if ((pw = getpwuid(getuid())) == NULL)
|
||||
errx(1, "who are you?");
|
||||
if (asprintf(&user, "%s", pw->pw_name) < 0)
|
||||
err(1, "asprintf()");
|
||||
issameuser = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no keyfile was specified on the command line, derive it from
|
||||
* the user name.
|
||||
*/
|
||||
if (keyfile == NULL)
|
||||
/* XXX replace with a function that searches multiple locations? */
|
||||
if (asprintf(&keyfile, "/var/oath/%s.otpauth", user) < 0)
|
||||
err(1, "asprintf()");
|
||||
|
||||
/*
|
||||
* Execute the requested command
|
||||
*/
|
||||
if (strcmp(cmd, "help") == 0)
|
||||
ret = RET_USAGE;
|
||||
else if (strcmp(cmd, "calc") == 0)
|
||||
ret = oathkey_calc(argc, argv);
|
||||
else if (strcmp(cmd, "genkey") == 0)
|
||||
ret = oathkey_genkey(argc, argv);
|
||||
else if (strcmp(cmd, "getkey") == 0)
|
||||
ret = oathkey_getkey(argc, argv);
|
||||
else if (strcmp(cmd, "geturi") == 0 || strcmp(cmd, "uri") == 0)
|
||||
ret = oathkey_geturi(argc, argv);
|
||||
else if (strcmp(cmd, "resync") == 0)
|
||||
ret = oathkey_resync(argc, argv);
|
||||
else if (strcmp(cmd, "setkey") == 0)
|
||||
ret = oathkey_setkey(argc, argv);
|
||||
else if (strcmp(cmd, "verify") == 0)
|
||||
ret = oathkey_verify(argc, argv);
|
||||
else
|
||||
ret = RET_USAGE;
|
||||
|
||||
/*
|
||||
* Check result and act accordingly
|
||||
*/
|
||||
switch (ret) {
|
||||
case RET_UNAUTH:
|
||||
errno = EPERM;
|
||||
err(1, "%s", cmd);
|
||||
break;
|
||||
case RET_USAGE:
|
||||
usage();
|
||||
break;
|
||||
case RET_SUCCESS:
|
||||
exit(0);
|
||||
break;
|
||||
case RET_FAILURE:
|
||||
exit(1);
|
||||
break;
|
||||
case RET_ERROR:
|
||||
exit(2);
|
||||
break;
|
||||
default:
|
||||
exit(3);
|
||||
break;
|
||||
}
|
||||
/* not reached */
|
||||
exit(255);
|
||||
}
|
22
configure.ac
22
configure.ac
|
@ -67,12 +67,6 @@ AC_ARG_WITH([pam-unix],
|
|||
[with_pam_unix=no])
|
||||
AM_CONDITIONAL([WITH_PAM_UNIX], [test x"$with_pam_unix" = x"yes"])
|
||||
|
||||
AC_ARG_WITH([oath],
|
||||
AC_HELP_STRING([--without-oath], [do not build OATH library, module and utility]),
|
||||
[],
|
||||
[with_oath=yes])
|
||||
AM_CONDITIONAL([WITH_OATH], [test x"$with_oath" = x"yes"])
|
||||
|
||||
AC_ARG_WITH(pamtest,
|
||||
AC_HELP_STRING([--with-pamtest], [build test application]),
|
||||
[],
|
||||
|
@ -91,12 +85,6 @@ AC_ARG_WITH(system-libpam,
|
|||
[with_system_libpam=no])
|
||||
AM_CONDITIONAL([WITH_SYSTEM_LIBPAM], [test x"$with_system_libpam" = x"yes"])
|
||||
|
||||
AC_ARG_WITH(system-liboath,
|
||||
AC_HELP_STRING([--with-system-liboath], [use system liboath]),
|
||||
[],
|
||||
[with_system_liboath=no])
|
||||
AM_CONDITIONAL([WITH_SYSTEM_LIBOATH], [test x"$with_system_liboath" = x"yes"])
|
||||
|
||||
AC_CHECK_HEADERS([crypt.h])
|
||||
|
||||
AC_CHECK_FUNCS([asprintf vasprintf])
|
||||
|
@ -133,13 +121,6 @@ SYSTEM_LIBPAM="${LIBS}"
|
|||
LIBS="${saved_LIBS}"
|
||||
AC_SUBST(SYSTEM_LIBPAM)
|
||||
|
||||
saved_LIBS="${LIBS}"
|
||||
LIBS=""
|
||||
AC_CHECK_LIB([oath], [oath_key_alloc])
|
||||
SYSTEM_LIBOATH="${LIBS}"
|
||||
LIBS="${saved_LIBS}"
|
||||
AC_SUBST(SYSTEM_LIBOATH)
|
||||
|
||||
AC_ARG_ENABLE([developer-warnings],
|
||||
AS_HELP_STRING([--enable-developer-warnings], [enable strict warnings (default is NO)]),
|
||||
[CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual"])
|
||||
|
@ -153,7 +134,6 @@ AC_ARG_ENABLE([werror],
|
|||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
bin/Makefile
|
||||
bin/oathkey/Makefile
|
||||
bin/openpam_dump_policy/Makefile
|
||||
bin/pamtest/Makefile
|
||||
bin/su/Makefile
|
||||
|
@ -162,14 +142,12 @@ AC_CONFIG_FILES([
|
|||
include/Makefile
|
||||
include/security/Makefile
|
||||
lib/Makefile
|
||||
lib/liboath/Makefile
|
||||
lib/libpam/Makefile
|
||||
modules/Makefile
|
||||
modules/pam_deny/Makefile
|
||||
modules/pam_permit/Makefile
|
||||
modules/pam_return/Makefile
|
||||
modules/pam_unix/Makefile
|
||||
modules/pam_oath/Makefile
|
||||
t/Makefile
|
||||
])
|
||||
AC_CONFIG_FILES([mkpkgng],[chmod +x mkpkgng])
|
||||
|
|
|
@ -61,37 +61,15 @@ OPENPAM_MAN = \
|
|||
pam_vprompt.3 \
|
||||
$(NULL)
|
||||
|
||||
if WITH_OATH
|
||||
if !WITH_SYSTEM_LIBOATH
|
||||
OATH_MAN = \
|
||||
oath_key_alloc.3 \
|
||||
oath_key_create.3 \
|
||||
oath_key_dummy.3 \
|
||||
oath_key_free.3 \
|
||||
oath_key_from_file.3 \
|
||||
oath_key_from_uri.3 \
|
||||
oath_mode.3 \
|
||||
oath_uri_decode.3 \
|
||||
$(NULL)
|
||||
endif
|
||||
endif
|
||||
|
||||
EXTRA_DIST = oath.man openpam.man pam.man
|
||||
EXTRA_DIST = openpam.man pam.man
|
||||
|
||||
if !WITH_SYSTEM_LIBPAM
|
||||
PAMCMAN = $(PAM_MAN) $(MOD_MAN) $(OPENPAM_MAN)
|
||||
PAMXMAN = openpam.3 pam.3
|
||||
endif
|
||||
|
||||
if WITH_OATH
|
||||
if !WITH_SYSTEM_LIBOATH
|
||||
OATHCMAN = $(OATH_MAN)
|
||||
OATHXMAN = oath.3
|
||||
endif
|
||||
endif
|
||||
|
||||
ALLCMAN = $(PAMCMAN) $(OATHCMAN)
|
||||
GENMAN = $(ALLCMAN) $(PAMXMAN) $(OATHXMAN)
|
||||
ALLCMAN = $(PAMCMAN)
|
||||
GENMAN = $(ALLCMAN) $(PAMXMAN)
|
||||
|
||||
dist_man3_MANS = $(GENMAN) pam_conv.3
|
||||
|
||||
|
@ -102,24 +80,14 @@ CLEANFILES = $(GENMAN)
|
|||
GENDOC = $(top_srcdir)/misc/gendoc.pl
|
||||
|
||||
LIBPAMSRCDIR = $(top_srcdir)/lib/libpam
|
||||
if WITH_OATH
|
||||
LIBOATHSRCDIR = $(top_srcdir)/lib/liboath
|
||||
endif
|
||||
|
||||
VPATH = $(LIBPAMSRCDIR) $(LIBOATHSRCDIR) $(srcdir)
|
||||
VPATH = $(LIBPAMSRCDIR) $(srcdir)
|
||||
|
||||
SUFFIXES = .3
|
||||
|
||||
.c.3: $(GENDOC)
|
||||
perl -w $(GENDOC) $< || rm $@
|
||||
|
||||
if WITH_OATH
|
||||
if !WITH_SYSTEM_LIBOATH
|
||||
oath.3: $(OATH_MAN) $(GENDOC) $(srcdir)/oath.man
|
||||
perl -w $(GENDOC) -a $(OATH_MAN) <$(srcdir)/oath.man || rm $@
|
||||
endif
|
||||
endif
|
||||
|
||||
openpam.3: $(OPENPAM_MAN) $(GENDOC) $(srcdir)/openpam.man
|
||||
perl -w $(GENDOC) -o $(OPENPAM_MAN) <$(srcdir)/openpam.man || rm $@
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
.\"
|
||||
.\" $Id$
|
||||
.\"
|
||||
.Sh DESCRIPTION
|
||||
The OATH authentication library implements the OATH HOTP and TOTP
|
||||
authentication methods.
|
1179
doc/rfc/rfc1321.txt
1179
doc/rfc/rfc1321.txt
File diff suppressed because it is too large
Load diff
|
@ -1,619 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group H. Krawczyk
|
||||
Request for Comments: 2104 IBM
|
||||
Category: Informational M. Bellare
|
||||
UCSD
|
||||
R. Canetti
|
||||
IBM
|
||||
February 1997
|
||||
|
||||
|
||||
HMAC: Keyed-Hashing for Message Authentication
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This memo provides information for the Internet community. This memo
|
||||
does not specify an Internet standard of any kind. Distribution of
|
||||
this memo is unlimited.
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes HMAC, a mechanism for message authentication
|
||||
using cryptographic hash functions. HMAC can be used with any
|
||||
iterative cryptographic hash function, e.g., MD5, SHA-1, in
|
||||
combination with a secret shared key. The cryptographic strength of
|
||||
HMAC depends on the properties of the underlying hash function.
|
||||
|
||||
1. Introduction
|
||||
|
||||
Providing a way to check the integrity of information transmitted
|
||||
over or stored in an unreliable medium is a prime necessity in the
|
||||
world of open computing and communications. Mechanisms that provide
|
||||
such integrity check based on a secret key are usually called
|
||||
"message authentication codes" (MAC). Typically, message
|
||||
authentication codes are used between two parties that share a secret
|
||||
key in order to validate information transmitted between these
|
||||
parties. In this document we present such a MAC mechanism based on
|
||||
cryptographic hash functions. This mechanism, called HMAC, is based
|
||||
on work by the authors [BCK1] where the construction is presented and
|
||||
cryptographically analyzed. We refer to that work for the details on
|
||||
the rationale and security analysis of HMAC, and its comparison to
|
||||
other keyed-hash methods.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 1]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
HMAC can be used in combination with any iterated cryptographic hash
|
||||
function. MD5 and SHA-1 are examples of such hash functions. HMAC
|
||||
also uses a secret key for calculation and verification of the
|
||||
message authentication values. The main goals behind this
|
||||
construction are
|
||||
|
||||
* To use, without modifications, available hash functions.
|
||||
In particular, hash functions that perform well in software,
|
||||
and for which code is freely and widely available.
|
||||
|
||||
* To preserve the original performance of the hash function without
|
||||
incurring a significant degradation.
|
||||
|
||||
* To use and handle keys in a simple way.
|
||||
|
||||
* To have a well understood cryptographic analysis of the strength of
|
||||
the authentication mechanism based on reasonable assumptions on the
|
||||
underlying hash function.
|
||||
|
||||
* To allow for easy replaceability of the underlying hash function in
|
||||
case that faster or more secure hash functions are found or
|
||||
required.
|
||||
|
||||
This document specifies HMAC using a generic cryptographic hash
|
||||
function (denoted by H). Specific instantiations of HMAC need to
|
||||
define a particular hash function. Current candidates for such hash
|
||||
functions include SHA-1 [SHA], MD5 [MD5], RIPEMD-128/160 [RIPEMD].
|
||||
These different realizations of HMAC will be denoted by HMAC-SHA1,
|
||||
HMAC-MD5, HMAC-RIPEMD, etc.
|
||||
|
||||
Note: To the date of writing of this document MD5 and SHA-1 are the
|
||||
most widely used cryptographic hash functions. MD5 has been recently
|
||||
shown to be vulnerable to collision search attacks [Dobb]. This
|
||||
attack and other currently known weaknesses of MD5 do not compromise
|
||||
the use of MD5 within HMAC as specified in this document (see
|
||||
[Dobb]); however, SHA-1 appears to be a cryptographically stronger
|
||||
function. To this date, MD5 can be considered for use in HMAC for
|
||||
applications where the superior performance of MD5 is critical. In
|
||||
any case, implementers and users need to be aware of possible
|
||||
cryptanalytic developments regarding any of these cryptographic hash
|
||||
functions, and the eventual need to replace the underlying hash
|
||||
function. (See section 6 for more information on the security of
|
||||
HMAC.)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 2]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
2. Definition of HMAC
|
||||
|
||||
The definition of HMAC requires a cryptographic hash function, which
|
||||
we denote by H, and a secret key K. We assume H to be a cryptographic
|
||||
hash function where data is hashed by iterating a basic compression
|
||||
function on blocks of data. We denote by B the byte-length of such
|
||||
blocks (B=64 for all the above mentioned examples of hash functions),
|
||||
and by L the byte-length of hash outputs (L=16 for MD5, L=20 for
|
||||
SHA-1). The authentication key K can be of any length up to B, the
|
||||
block length of the hash function. Applications that use keys longer
|
||||
than B bytes will first hash the key using H and then use the
|
||||
resultant L byte string as the actual key to HMAC. In any case the
|
||||
minimal recommended length for K is L bytes (as the hash output
|
||||
length). See section 3 for more information on keys.
|
||||
|
||||
We define two fixed and different strings ipad and opad as follows
|
||||
(the 'i' and 'o' are mnemonics for inner and outer):
|
||||
|
||||
ipad = the byte 0x36 repeated B times
|
||||
opad = the byte 0x5C repeated B times.
|
||||
|
||||
To compute HMAC over the data `text' we perform
|
||||
|
||||
H(K XOR opad, H(K XOR ipad, text))
|
||||
|
||||
Namely,
|
||||
|
||||
(1) append zeros to the end of K to create a B byte string
|
||||
(e.g., if K is of length 20 bytes and B=64, then K will be
|
||||
appended with 44 zero bytes 0x00)
|
||||
(2) XOR (bitwise exclusive-OR) the B byte string computed in step
|
||||
(1) with ipad
|
||||
(3) append the stream of data 'text' to the B byte string resulting
|
||||
from step (2)
|
||||
(4) apply H to the stream generated in step (3)
|
||||
(5) XOR (bitwise exclusive-OR) the B byte string computed in
|
||||
step (1) with opad
|
||||
(6) append the H result from step (4) to the B byte string
|
||||
resulting from step (5)
|
||||
(7) apply H to the stream generated in step (6) and output
|
||||
the result
|
||||
|
||||
For illustration purposes, sample code based on MD5 is provided as an
|
||||
appendix.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 3]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
3. Keys
|
||||
|
||||
The key for HMAC can be of any length (keys longer than B bytes are
|
||||
first hashed using H). However, less than L bytes is strongly
|
||||
discouraged as it would decrease the security strength of the
|
||||
function. Keys longer than L bytes are acceptable but the extra
|
||||
length would not significantly increase the function strength. (A
|
||||
longer key may be advisable if the randomness of the key is
|
||||
considered weak.)
|
||||
|
||||
Keys need to be chosen at random (or using a cryptographically strong
|
||||
pseudo-random generator seeded with a random seed), and periodically
|
||||
refreshed. (Current attacks do not indicate a specific recommended
|
||||
frequency for key changes as these attacks are practically
|
||||
infeasible. However, periodic key refreshment is a fundamental
|
||||
security practice that helps against potential weaknesses of the
|
||||
function and keys, and limits the damage of an exposed key.)
|
||||
|
||||
4. Implementation Note
|
||||
|
||||
HMAC is defined in such a way that the underlying hash function H can
|
||||
be used with no modification to its code. In particular, it uses the
|
||||
function H with the pre-defined initial value IV (a fixed value
|
||||
specified by each iterative hash function to initialize its
|
||||
compression function). However, if desired, a performance
|
||||
improvement can be achieved at the cost of (possibly) modifying the
|
||||
code of H to support variable IVs.
|
||||
|
||||
The idea is that the intermediate results of the compression function
|
||||
on the B-byte blocks (K XOR ipad) and (K XOR opad) can be precomputed
|
||||
only once at the time of generation of the key K, or before its first
|
||||
use. These intermediate results are stored and then used to
|
||||
initialize the IV of H each time that a message needs to be
|
||||
authenticated. This method saves, for each authenticated message,
|
||||
the application of the compression function of H on two B-byte blocks
|
||||
(i.e., on (K XOR ipad) and (K XOR opad)). Such a savings may be
|
||||
significant when authenticating short streams of data. We stress
|
||||
that the stored intermediate values need to be treated and protected
|
||||
the same as secret keys.
|
||||
|
||||
Choosing to implement HMAC in the above way is a decision of the
|
||||
local implementation and has no effect on inter-operability.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 4]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
5. Truncated output
|
||||
|
||||
A well-known practice with message authentication codes is to
|
||||
truncate the output of the MAC and output only part of the bits
|
||||
(e.g., [MM, ANSI]). Preneel and van Oorschot [PV] show some
|
||||
analytical advantages of truncating the output of hash-based MAC
|
||||
functions. The results in this area are not absolute as for the
|
||||
overall security advantages of truncation. It has advantages (less
|
||||
information on the hash result available to an attacker) and
|
||||
disadvantages (less bits to predict for the attacker). Applications
|
||||
of HMAC can choose to truncate the output of HMAC by outputting the t
|
||||
leftmost bits of the HMAC computation for some parameter t (namely,
|
||||
the computation is carried in the normal way as defined in section 2
|
||||
above but the end result is truncated to t bits). We recommend that
|
||||
the output length t be not less than half the length of the hash
|
||||
output (to match the birthday attack bound) and not less than 80 bits
|
||||
(a suitable lower bound on the number of bits that need to be
|
||||
predicted by an attacker). We propose denoting a realization of HMAC
|
||||
that uses a hash function H with t bits of output as HMAC-H-t. For
|
||||
example, HMAC-SHA1-80 denotes HMAC computed using the SHA-1 function
|
||||
and with the output truncated to 80 bits. (If the parameter t is not
|
||||
specified, e.g. HMAC-MD5, then it is assumed that all the bits of the
|
||||
hash are output.)
|
||||
|
||||
6. Security
|
||||
|
||||
The security of the message authentication mechanism presented here
|
||||
depends on cryptographic properties of the hash function H: the
|
||||
resistance to collision finding (limited to the case where the
|
||||
initial value is secret and random, and where the output of the
|
||||
function is not explicitly available to the attacker), and the
|
||||
message authentication property of the compression function of H when
|
||||
applied to single blocks (in HMAC these blocks are partially unknown
|
||||
to an attacker as they contain the result of the inner H computation
|
||||
and, in particular, cannot be fully chosen by the attacker).
|
||||
|
||||
These properties, and actually stronger ones, are commonly assumed
|
||||
for hash functions of the kind used with HMAC. In particular, a hash
|
||||
function for which the above properties do not hold would become
|
||||
unsuitable for most (probably, all) cryptographic applications,
|
||||
including alternative message authentication schemes based on such
|
||||
functions. (For a complete analysis and rationale of the HMAC
|
||||
function the reader is referred to [BCK1].)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 5]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
Given the limited confidence gained so far as for the cryptographic
|
||||
strength of candidate hash functions, it is important to observe the
|
||||
following two properties of the HMAC construction and its secure use
|
||||
for message authentication:
|
||||
|
||||
1. The construction is independent of the details of the particular
|
||||
hash function H in use and then the latter can be replaced by any
|
||||
other secure (iterative) cryptographic hash function.
|
||||
|
||||
2. Message authentication, as opposed to encryption, has a
|
||||
"transient" effect. A published breaking of a message authentication
|
||||
scheme would lead to the replacement of that scheme, but would have
|
||||
no adversarial effect on information authenticated in the past. This
|
||||
is in sharp contrast with encryption, where information encrypted
|
||||
today may suffer from exposure in the future if, and when, the
|
||||
encryption algorithm is broken.
|
||||
|
||||
The strongest attack known against HMAC is based on the frequency of
|
||||
collisions for the hash function H ("birthday attack") [PV,BCK2], and
|
||||
is totally impractical for minimally reasonable hash functions.
|
||||
|
||||
As an example, if we consider a hash function like MD5 where the
|
||||
output length equals L=16 bytes (128 bits) the attacker needs to
|
||||
acquire the correct message authentication tags computed (with the
|
||||
_same_ secret key K!) on about 2**64 known plaintexts. This would
|
||||
require the processing of at least 2**64 blocks under H, an
|
||||
impossible task in any realistic scenario (for a block length of 64
|
||||
bytes this would take 250,000 years in a continuous 1Gbps link, and
|
||||
without changing the secret key K during all this time). This attack
|
||||
could become realistic only if serious flaws in the collision
|
||||
behavior of the function H are discovered (e.g. collisions found
|
||||
after 2**30 messages). Such a discovery would determine the immediate
|
||||
replacement of the function H (the effects of such failure would be
|
||||
far more severe for the traditional uses of H in the context of
|
||||
digital signatures, public key certificates, etc.).
|
||||
|
||||
Note: this attack needs to be strongly contrasted with regular
|
||||
collision attacks on cryptographic hash functions where no secret key
|
||||
is involved and where 2**64 off-line parallelizable (!) operations
|
||||
suffice to find collisions. The latter attack is approaching
|
||||
feasibility [VW] while the birthday attack on HMAC is totally
|
||||
impractical. (In the above examples, if one uses a hash function
|
||||
with, say, 160 bit of output then 2**64 should be replaced by 2**80.)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 6]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
A correct implementation of the above construction, the choice of
|
||||
random (or cryptographically pseudorandom) keys, a secure key
|
||||
exchange mechanism, frequent key refreshments, and good secrecy
|
||||
protection of keys are all essential ingredients for the security of
|
||||
the integrity verification mechanism provided by HMAC.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 7]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
Appendix -- Sample Code
|
||||
|
||||
For the sake of illustration we provide the following sample code for
|
||||
the implementation of HMAC-MD5 as well as some corresponding test
|
||||
vectors (the code is based on MD5 code as described in [MD5]).
|
||||
|
||||
/*
|
||||
** Function: hmac_md5
|
||||
*/
|
||||
|
||||
void
|
||||
hmac_md5(text, text_len, key, key_len, digest)
|
||||
unsigned char* text; /* pointer to data stream */
|
||||
int text_len; /* length of data stream */
|
||||
unsigned char* key; /* pointer to authentication key */
|
||||
int key_len; /* length of authentication key */
|
||||
caddr_t digest; /* caller digest to be filled in */
|
||||
|
||||
{
|
||||
MD5_CTX context;
|
||||
unsigned char k_ipad[65]; /* inner padding -
|
||||
* key XORd with ipad
|
||||
*/
|
||||
unsigned char k_opad[65]; /* outer padding -
|
||||
* key XORd with opad
|
||||
*/
|
||||
unsigned char tk[16];
|
||||
int i;
|
||||
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
||||
if (key_len > 64) {
|
||||
|
||||
MD5_CTX tctx;
|
||||
|
||||
MD5Init(&tctx);
|
||||
MD5Update(&tctx, key, key_len);
|
||||
MD5Final(tk, &tctx);
|
||||
|
||||
key = tk;
|
||||
key_len = 16;
|
||||
}
|
||||
|
||||
/*
|
||||
* the HMAC_MD5 transform looks like:
|
||||
*
|
||||
* MD5(K XOR opad, MD5(K XOR ipad, text))
|
||||
*
|
||||
* where K is an n byte key
|
||||
* ipad is the byte 0x36 repeated 64 times
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 8]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
* opad is the byte 0x5c repeated 64 times
|
||||
* and text is the data being protected
|
||||
*/
|
||||
|
||||
/* start out by storing key in pads */
|
||||
bzero( k_ipad, sizeof k_ipad);
|
||||
bzero( k_opad, sizeof k_opad);
|
||||
bcopy( key, k_ipad, key_len);
|
||||
bcopy( key, k_opad, key_len);
|
||||
|
||||
/* XOR key with ipad and opad values */
|
||||
for (i=0; i<64; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
/*
|
||||
* perform inner MD5
|
||||
*/
|
||||
MD5Init(&context); /* init context for 1st
|
||||
* pass */
|
||||
MD5Update(&context, k_ipad, 64) /* start with inner pad */
|
||||
MD5Update(&context, text, text_len); /* then text of datagram */
|
||||
MD5Final(digest, &context); /* finish up 1st pass */
|
||||
/*
|
||||
* perform outer MD5
|
||||
*/
|
||||
MD5Init(&context); /* init context for 2nd
|
||||
* pass */
|
||||
MD5Update(&context, k_opad, 64); /* start with outer pad */
|
||||
MD5Update(&context, digest, 16); /* then results of 1st
|
||||
* hash */
|
||||
MD5Final(digest, &context); /* finish up 2nd pass */
|
||||
}
|
||||
|
||||
Test Vectors (Trailing '\0' of a character string not included in test):
|
||||
|
||||
key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
|
||||
key_len = 16 bytes
|
||||
data = "Hi There"
|
||||
data_len = 8 bytes
|
||||
digest = 0x9294727a3638bb1c13f48ef8158bfc9d
|
||||
|
||||
key = "Jefe"
|
||||
data = "what do ya want for nothing?"
|
||||
data_len = 28 bytes
|
||||
digest = 0x750c783e6ab0b503eaa86e310a5db738
|
||||
|
||||
key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 9]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
key_len 16 bytes
|
||||
data = 0xDDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD
|
||||
data_len = 50 bytes
|
||||
digest = 0x56be34521d144c88dbb8c733f0e8b3f6
|
||||
|
||||
Acknowledgments
|
||||
|
||||
Pau-Chen Cheng, Jeff Kraemer, and Michael Oehler, have provided
|
||||
useful comments on early drafts, and ran the first interoperability
|
||||
tests of this specification. Jeff and Pau-Chen kindly provided the
|
||||
sample code and test vectors that appear in the appendix. Burt
|
||||
Kaliski, Bart Preneel, Matt Robshaw, Adi Shamir, and Paul van
|
||||
Oorschot have provided useful comments and suggestions during the
|
||||
investigation of the HMAC construction.
|
||||
|
||||
References
|
||||
|
||||
[ANSI] ANSI X9.9, "American National Standard for Financial
|
||||
Institution Message Authentication (Wholesale)," American
|
||||
Bankers Association, 1981. Revised 1986.
|
||||
|
||||
[Atk] Atkinson, R., "IP Authentication Header", RFC 1826, August
|
||||
1995.
|
||||
|
||||
[BCK1] M. Bellare, R. Canetti, and H. Krawczyk,
|
||||
"Keyed Hash Functions and Message Authentication",
|
||||
Proceedings of Crypto'96, LNCS 1109, pp. 1-15.
|
||||
(http://www.research.ibm.com/security/keyed-md5.html)
|
||||
|
||||
[BCK2] M. Bellare, R. Canetti, and H. Krawczyk,
|
||||
"Pseudorandom Functions Revisited: The Cascade Construction",
|
||||
Proceedings of FOCS'96.
|
||||
|
||||
[Dobb] H. Dobbertin, "The Status of MD5 After a Recent Attack",
|
||||
RSA Labs' CryptoBytes, Vol. 2 No. 2, Summer 1996.
|
||||
http://www.rsa.com/rsalabs/pubs/cryptobytes.html
|
||||
|
||||
[PV] B. Preneel and P. van Oorschot, "Building fast MACs from hash
|
||||
functions", Advances in Cryptology -- CRYPTO'95 Proceedings,
|
||||
Lecture Notes in Computer Science, Springer-Verlag Vol.963,
|
||||
1995, pp. 1-14.
|
||||
|
||||
[MD5] Rivest, R., "The MD5 Message-Digest Algorithm",
|
||||
RFC 1321, April 1992.
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 10]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
[MM] Meyer, S. and Matyas, S.M., Cryptography, New York Wiley,
|
||||
1982.
|
||||
|
||||
[RIPEMD] H. Dobbertin, A. Bosselaers, and B. Preneel, "RIPEMD-160: A
|
||||
strengthened version of RIPEMD", Fast Software Encryption,
|
||||
LNCS Vol 1039, pp. 71-82.
|
||||
ftp://ftp.esat.kuleuven.ac.be/pub/COSIC/bosselae/ripemd/.
|
||||
|
||||
[SHA] NIST, FIPS PUB 180-1: Secure Hash Standard, April 1995.
|
||||
|
||||
[Tsu] G. Tsudik, "Message authentication with one-way hash
|
||||
functions", In Proceedings of Infocom'92, May 1992.
|
||||
(Also in "Access Control and Policy Enforcement in
|
||||
Internetworks", Ph.D. Dissertation, Computer Science
|
||||
Department, University of Southern California, April 1991.)
|
||||
|
||||
[VW] P. van Oorschot and M. Wiener, "Parallel Collision
|
||||
Search with Applications to Hash Functions and Discrete
|
||||
Logarithms", Proceedings of the 2nd ACM Conf. Computer and
|
||||
Communications Security, Fairfax, VA, November 1994.
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Hugo Krawczyk
|
||||
IBM T.J. Watson Research Center
|
||||
P.O.Box 704
|
||||
Yorktown Heights, NY 10598
|
||||
|
||||
EMail: hugo@watson.ibm.com
|
||||
|
||||
Mihir Bellare
|
||||
Dept of Computer Science and Engineering
|
||||
Mail Code 0114
|
||||
University of California at San Diego
|
||||
9500 Gilman Drive
|
||||
La Jolla, CA 92093
|
||||
|
||||
EMail: mihir@cs.ucsd.edu
|
||||
|
||||
Ran Canetti
|
||||
IBM T.J. Watson Research Center
|
||||
P.O.Box 704
|
||||
Yorktown Heights, NY 10598
|
||||
|
||||
EMail: canetti@watson.ibm.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 11]
|
||||
|
2075
doc/rfc/rfc4226.txt
2075
doc/rfc/rfc4226.txt
File diff suppressed because it is too large
Load diff
1011
doc/rfc/rfc4648.txt
1011
doc/rfc/rfc4648.txt
File diff suppressed because it is too large
Load diff
7115
doc/rfc/rfc6234.txt
7115
doc/rfc/rfc6234.txt
File diff suppressed because it is too large
Load diff
|
@ -1,899 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force (IETF) D. M'Raihi
|
||||
Request for Comments: 6238 Verisign, Inc.
|
||||
Category: Informational S. Machani
|
||||
ISSN: 2070-1721 Diversinet Corp.
|
||||
M. Pei
|
||||
Symantec
|
||||
J. Rydell
|
||||
Portwise, Inc.
|
||||
May 2011
|
||||
|
||||
|
||||
TOTP: Time-Based One-Time Password Algorithm
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes an extension of the One-Time Password (OTP)
|
||||
algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm,
|
||||
as defined in RFC 4226, to support the time-based moving factor. The
|
||||
HOTP algorithm specifies an event-based OTP algorithm, where the
|
||||
moving factor is an event counter. The present work bases the moving
|
||||
factor on a time value. A time-based variant of the OTP algorithm
|
||||
provides short-lived OTP values, which are desirable for enhanced
|
||||
security.
|
||||
|
||||
The proposed algorithm can be used across a wide range of network
|
||||
applications, from remote Virtual Private Network (VPN) access and
|
||||
Wi-Fi network logon to transaction-oriented Web applications. The
|
||||
authors believe that a common and shared algorithm will facilitate
|
||||
adoption of two-factor authentication on the Internet by enabling
|
||||
interoperability across commercial and open-source implementations.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This document is not an Internet Standards Track specification; it is
|
||||
published for informational purposes.
|
||||
|
||||
This document is a product of the Internet Engineering Task Force
|
||||
(IETF). It represents the consensus of the IETF community. It has
|
||||
received public review and has been approved for publication by the
|
||||
Internet Engineering Steering Group (IESG). Not all documents
|
||||
approved by the IESG are a candidate for any level of Internet
|
||||
Standard; see Section 2 of RFC 5741.
|
||||
|
||||
Information about the current status of this document, any errata,
|
||||
and how to provide feedback on it may be obtained at
|
||||
http://www.rfc-editor.org/info/rfc6238.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 1]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction ....................................................2
|
||||
1.1. Scope ......................................................2
|
||||
1.2. Background .................................................3
|
||||
2. Notation and Terminology ........................................3
|
||||
3. Algorithm Requirements ..........................................3
|
||||
4. TOTP Algorithm ..................................................4
|
||||
4.1. Notations ..................................................4
|
||||
4.2. Description ................................................4
|
||||
5. Security Considerations .........................................5
|
||||
5.1. General ....................................................5
|
||||
5.2. Validation and Time-Step Size ..............................6
|
||||
6. Resynchronization ...............................................7
|
||||
7. Acknowledgements ................................................7
|
||||
8. References ......................................................8
|
||||
8.1. Normative References .......................................8
|
||||
8.2. Informative References .....................................8
|
||||
Appendix A. TOTP Algorithm: Reference Implementation ...............9
|
||||
Appendix B. Test Vectors ..........................................14
|
||||
|
||||
1. Introduction
|
||||
|
||||
1.1. Scope
|
||||
|
||||
This document describes an extension of the One-Time Password (OTP)
|
||||
algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm,
|
||||
as defined in [RFC4226], to support the time-based moving factor.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 2]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
1.2. Background
|
||||
|
||||
As defined in [RFC4226], the HOTP algorithm is based on the
|
||||
HMAC-SHA-1 algorithm (as specified in [RFC2104]) and applied to an
|
||||
increasing counter value representing the message in the HMAC
|
||||
computation.
|
||||
|
||||
Basically, the output of the HMAC-SHA-1 calculation is truncated to
|
||||
obtain user-friendly values:
|
||||
|
||||
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
|
||||
|
||||
where Truncate represents the function that can convert an HMAC-SHA-1
|
||||
value into an HOTP value. K and C represent the shared secret and
|
||||
counter value; see [RFC4226] for detailed definitions.
|
||||
|
||||
TOTP is the time-based variant of this algorithm, where a value T,
|
||||
derived from a time reference and a time step, replaces the counter C
|
||||
in the HOTP computation.
|
||||
|
||||
TOTP implementations MAY use HMAC-SHA-256 or HMAC-SHA-512 functions,
|
||||
based on SHA-256 or SHA-512 [SHA2] hash functions, instead of the
|
||||
HMAC-SHA-1 function that has been specified for the HOTP computation
|
||||
in [RFC4226].
|
||||
|
||||
2. Notation and Terminology
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
3. Algorithm Requirements
|
||||
|
||||
This section summarizes the requirements taken into account for
|
||||
designing the TOTP algorithm.
|
||||
|
||||
R1: The prover (e.g., token, soft token) and verifier (authentication
|
||||
or validation server) MUST know or be able to derive the current
|
||||
Unix time (i.e., the number of seconds elapsed since midnight UTC
|
||||
of January 1, 1970) for OTP generation. See [UT] for a more
|
||||
detailed definition of the commonly known "Unix time". The
|
||||
precision of the time used by the prover affects how often the
|
||||
clock synchronization should be done; see Section 6.
|
||||
|
||||
R2: The prover and verifier MUST either share the same secret or the
|
||||
knowledge of a secret transformation to generate a shared secret.
|
||||
|
||||
R3: The algorithm MUST use HOTP [RFC4226] as a key building block.
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 3]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
R4: The prover and verifier MUST use the same time-step value X.
|
||||
|
||||
R5: There MUST be a unique secret (key) for each prover.
|
||||
|
||||
R6: The keys SHOULD be randomly generated or derived using key
|
||||
derivation algorithms.
|
||||
|
||||
R7: The keys MAY be stored in a tamper-resistant device and SHOULD be
|
||||
protected against unauthorized access and usage.
|
||||
|
||||
4. TOTP Algorithm
|
||||
|
||||
This variant of the HOTP algorithm specifies the calculation of a
|
||||
one-time password value, based on a representation of the counter as
|
||||
a time factor.
|
||||
|
||||
4.1. Notations
|
||||
|
||||
o X represents the time step in seconds (default value X =
|
||||
30 seconds) and is a system parameter.
|
||||
|
||||
o T0 is the Unix time to start counting time steps (default value is
|
||||
0, i.e., the Unix epoch) and is also a system parameter.
|
||||
|
||||
4.2. Description
|
||||
|
||||
Basically, we define TOTP as TOTP = HOTP(K, T), where T is an integer
|
||||
and represents the number of time steps between the initial counter
|
||||
time T0 and the current Unix time.
|
||||
|
||||
More specifically, T = (Current Unix time - T0) / X, where the
|
||||
default floor function is used in the computation.
|
||||
|
||||
For example, with T0 = 0 and Time Step X = 30, T = 1 if the current
|
||||
Unix time is 59 seconds, and T = 2 if the current Unix time is
|
||||
60 seconds.
|
||||
|
||||
The implementation of this algorithm MUST support a time value T
|
||||
larger than a 32-bit integer when it is beyond the year 2038. The
|
||||
value of the system parameters X and T0 are pre-established during
|
||||
the provisioning process and communicated between a prover and
|
||||
verifier as part of the provisioning step. The provisioning flow is
|
||||
out of scope of this document; refer to [RFC6030] for such
|
||||
provisioning container specifications.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 4]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
5. Security Considerations
|
||||
|
||||
5.1. General
|
||||
|
||||
The security and strength of this algorithm depend on the properties
|
||||
of the underlying building block HOTP, which is a construction based
|
||||
on HMAC [RFC2104] using SHA-1 as the hash function.
|
||||
|
||||
The conclusion of the security analysis detailed in [RFC4226] is
|
||||
that, for all practical purposes, the outputs of the dynamic
|
||||
truncation on distinct inputs are uniformly and independently
|
||||
distributed strings.
|
||||
|
||||
The analysis demonstrates that the best possible attack against the
|
||||
HOTP function is the brute force attack.
|
||||
|
||||
As indicated in the algorithm requirement section, keys SHOULD be
|
||||
chosen at random or using a cryptographically strong pseudorandom
|
||||
generator properly seeded with a random value.
|
||||
|
||||
Keys SHOULD be of the length of the HMAC output to facilitate
|
||||
interoperability.
|
||||
|
||||
We RECOMMEND following the recommendations in [RFC4086] for all
|
||||
pseudorandom and random number generations. The pseudorandom numbers
|
||||
used for generating the keys SHOULD successfully pass the randomness
|
||||
test specified in [CN], or a similar well-recognized test.
|
||||
|
||||
All the communications SHOULD take place over a secure channel, e.g.,
|
||||
Secure Socket Layer/Transport Layer Security (SSL/TLS) [RFC5246] or
|
||||
IPsec connections [RFC4301].
|
||||
|
||||
We also RECOMMEND storing the keys securely in the validation system,
|
||||
and, more specifically, encrypting them using tamper-resistant
|
||||
hardware encryption and exposing them only when required: for
|
||||
example, the key is decrypted when needed to verify an OTP value, and
|
||||
re-encrypted immediately to limit exposure in the RAM to a short
|
||||
period of time.
|
||||
|
||||
The key store MUST be in a secure area, to avoid, as much as
|
||||
possible, direct attack on the validation system and secrets
|
||||
database. Particularly, access to the key material should be limited
|
||||
to programs and processes required by the validation system only.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 5]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
5.2. Validation and Time-Step Size
|
||||
|
||||
An OTP generated within the same time step will be the same. When an
|
||||
OTP is received at a validation system, it doesn't know a client's
|
||||
exact timestamp when an OTP was generated. The validation system may
|
||||
typically use the timestamp when an OTP is received for OTP
|
||||
comparison. Due to network latency, the gap (as measured by T, that
|
||||
is, the number of time steps since T0) between the time that the OTP
|
||||
was generated and the time that the OTP arrives at the receiving
|
||||
system may be large. The receiving time at the validation system and
|
||||
the actual OTP generation may not fall within the same time-step
|
||||
window that produced the same OTP. When an OTP is generated at the
|
||||
end of a time-step window, the receiving time most likely falls into
|
||||
the next time-step window. A validation system SHOULD typically set
|
||||
a policy for an acceptable OTP transmission delay window for
|
||||
validation. The validation system should compare OTPs not only with
|
||||
the receiving timestamp but also the past timestamps that are within
|
||||
the transmission delay. A larger acceptable delay window would
|
||||
expose a larger window for attacks. We RECOMMEND that at most one
|
||||
time step is allowed as the network delay.
|
||||
|
||||
The time-step size has an impact on both security and usability. A
|
||||
larger time-step size means a larger validity window for an OTP to be
|
||||
accepted by a validation system. There are implications for using a
|
||||
larger time-step size, as follows:
|
||||
|
||||
First, a larger time-step size exposes a larger window to attack.
|
||||
When an OTP is generated and exposed to a third party before it is
|
||||
consumed, the third party can consume the OTP within the time-step
|
||||
window.
|
||||
|
||||
We RECOMMEND a default time-step size of 30 seconds. This default
|
||||
value of 30 seconds is selected as a balance between security and
|
||||
usability.
|
||||
|
||||
Second, the next different OTP must be generated in the next time-
|
||||
step window. A user must wait until the clock moves to the next
|
||||
time-step window from the last submission. The waiting time may not
|
||||
be exactly the length of the time step, depending on when the last
|
||||
OTP was generated. For example, if the last OTP was generated at the
|
||||
halfway point in a time-step window, the waiting time for the next
|
||||
OTP is half the length of the time step. In general, a larger time-
|
||||
step window means a longer waiting time for a user to get the next
|
||||
valid OTP after the last successful OTP validation. A too-large
|
||||
window (for example, 10 minutes) most probably won't be suitable for
|
||||
typical Internet login use cases; a user may not be able to get the
|
||||
next OTP within 10 minutes and therefore will have to re-login to the
|
||||
same site in 10 minutes.
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 6]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Note that a prover may send the same OTP inside a given time-step
|
||||
window multiple times to a verifier. The verifier MUST NOT accept
|
||||
the second attempt of the OTP after the successful validation has
|
||||
been issued for the first OTP, which ensures one-time only use of an
|
||||
OTP.
|
||||
|
||||
6. Resynchronization
|
||||
|
||||
Because of possible clock drifts between a client and a validation
|
||||
server, we RECOMMEND that the validator be set with a specific limit
|
||||
to the number of time steps a prover can be "out of synch" before
|
||||
being rejected.
|
||||
|
||||
This limit can be set both forward and backward from the calculated
|
||||
time step on receipt of the OTP value. If the time step is
|
||||
30 seconds as recommended, and the validator is set to only accept
|
||||
two time steps backward, then the maximum elapsed time drift would be
|
||||
around 89 seconds, i.e., 29 seconds in the calculated time step and
|
||||
60 seconds for two backward time steps.
|
||||
|
||||
This would mean the validator could perform a validation against the
|
||||
current time and then two further validations for each backward step
|
||||
(for a total of 3 validations). Upon successful validation, the
|
||||
validation server can record the detected clock drift for the token
|
||||
in terms of the number of time steps. When a new OTP is received
|
||||
after this step, the validator can validate the OTP with the current
|
||||
timestamp adjusted with the recorded number of time-step clock drifts
|
||||
for the token.
|
||||
|
||||
Also, it is important to note that the longer a prover has not sent
|
||||
an OTP to a validation system, the longer (potentially) the
|
||||
accumulated clock drift between the prover and the verifier. In such
|
||||
cases, the automatic resynchronization described above may not work
|
||||
if the drift exceeds the allowed threshold. Additional
|
||||
authentication measures should be used to safely authenticate the
|
||||
prover and explicitly resynchronize the clock drift between the
|
||||
prover and the validator.
|
||||
|
||||
7. Acknowledgements
|
||||
|
||||
The authors of this document would like to thank the following people
|
||||
for their contributions and support to make this a better
|
||||
specification: Hannes Tschofenig, Jonathan Tuliani, David Dix,
|
||||
Siddharth Bajaj, Stu Veath, Shuh Chang, Oanh Hoang, John Huang, and
|
||||
Siddhartha Mohapatra.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 7]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
8. References
|
||||
|
||||
8.1. Normative References
|
||||
|
||||
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
|
||||
Hashing for Message Authentication", RFC 2104,
|
||||
February 1997.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker,
|
||||
"Randomness Recommendations for Security", BCP 106,
|
||||
RFC 4086, June 2005.
|
||||
|
||||
[RFC4226] M'Raihi, D., Bellare, M., Hoornaert, F., Naccache, D., and
|
||||
O. Ranen, "HOTP: An HMAC-Based One-Time Password
|
||||
Algorithm", RFC 4226, December 2005.
|
||||
|
||||
[SHA2] NIST, "FIPS PUB 180-3: Secure Hash Standard (SHS)",
|
||||
October 2008, <http://csrc.nist.gov/publications/fips/
|
||||
fips180-3/fips180-3_final.pdf>.
|
||||
|
||||
8.2. Informative References
|
||||
|
||||
[CN] Coron, J. and D. Naccache, "An Accurate Evaluation of
|
||||
Maurer's Universal Test", LNCS 1556, February 1999,
|
||||
<http://www.gemplus.com/smart/rd/publications/pdf/
|
||||
CN99maur.pdf>.
|
||||
|
||||
[RFC4301] Kent, S. and K. Seo, "Security Architecture for the
|
||||
Internet Protocol", RFC 4301, December 2005.
|
||||
|
||||
[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
|
||||
(TLS) Protocol Version 1.2", RFC 5246, August 2008.
|
||||
|
||||
[RFC6030] Hoyer, P., Pei, M., and S. Machani, "Portable Symmetric
|
||||
Key Container (PSKC)", RFC 6030, October 2010.
|
||||
|
||||
[UT] Wikipedia, "Unix time", February 2011,
|
||||
<http://en.wikipedia.org/wiki/Unix_time>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 8]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Appendix A. TOTP Algorithm: Reference Implementation
|
||||
|
||||
<CODE BEGINS>
|
||||
|
||||
/**
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted pursuant to, and subject to the license
|
||||
terms contained in, the Simplified BSD License set forth in Section
|
||||
4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
*/
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
* This is an example implementation of the OATH
|
||||
* TOTP algorithm.
|
||||
* Visit www.openauthentication.org for more information.
|
||||
*
|
||||
* @author Johan Rydell, PortWise, Inc.
|
||||
*/
|
||||
|
||||
public class TOTP {
|
||||
|
||||
private TOTP() {}
|
||||
|
||||
/**
|
||||
* This method uses the JCE to provide the crypto algorithm.
|
||||
* HMAC computes a Hashed Message Authentication Code with the
|
||||
* crypto hash algorithm as a parameter.
|
||||
*
|
||||
* @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256,
|
||||
* HmacSHA512)
|
||||
* @param keyBytes: the bytes to use for the HMAC key
|
||||
* @param text: the message or text to be authenticated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 9]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
private static byte[] hmac_sha(String crypto, byte[] keyBytes,
|
||||
byte[] text){
|
||||
try {
|
||||
Mac hmac;
|
||||
hmac = Mac.getInstance(crypto);
|
||||
SecretKeySpec macKey =
|
||||
new SecretKeySpec(keyBytes, "RAW");
|
||||
hmac.init(macKey);
|
||||
return hmac.doFinal(text);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new UndeclaredThrowableException(gse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method converts a HEX string to Byte[]
|
||||
*
|
||||
* @param hex: the HEX string
|
||||
*
|
||||
* @return: a byte array
|
||||
*/
|
||||
|
||||
private static byte[] hexStr2Bytes(String hex){
|
||||
// Adding one byte to get the right conversion
|
||||
// Values starting with "0" can be converted
|
||||
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
|
||||
|
||||
// Copy all the REAL bytes, not the "first"
|
||||
byte[] ret = new byte[bArray.length - 1];
|
||||
for (int i = 0; i < ret.length; i++)
|
||||
ret[i] = bArray[i+1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final int[] DIGITS_POWER
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 10]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA1");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP256(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA256");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 11]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP512(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA512");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
* @param crypto: the crypto function to use
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key,
|
||||
String time,
|
||||
String returnDigits,
|
||||
String crypto){
|
||||
int codeDigits = Integer.decode(returnDigits).intValue();
|
||||
String result = null;
|
||||
|
||||
// Using the counter
|
||||
// First 8 bytes are for the movingFactor
|
||||
// Compliant with base RFC 4226 (HOTP)
|
||||
while (time.length() < 16 )
|
||||
time = "0" + time;
|
||||
|
||||
// Get the HEX in a Byte[]
|
||||
byte[] msg = hexStr2Bytes(time);
|
||||
byte[] k = hexStr2Bytes(key);
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 12]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
byte[] hash = hmac_sha(crypto, k, msg);
|
||||
|
||||
// put selected bytes into result int
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
|
||||
int binary =
|
||||
((hash[offset] & 0x7f) << 24) |
|
||||
((hash[offset + 1] & 0xff) << 16) |
|
||||
((hash[offset + 2] & 0xff) << 8) |
|
||||
(hash[offset + 3] & 0xff);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < codeDigits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Seed for HMAC-SHA1 - 20 bytes
|
||||
String seed = "3132333435363738393031323334353637383930";
|
||||
// Seed for HMAC-SHA256 - 32 bytes
|
||||
String seed32 = "3132333435363738393031323334353637383930" +
|
||||
"313233343536373839303132";
|
||||
// Seed for HMAC-SHA512 - 64 bytes
|
||||
String seed64 = "3132333435363738393031323334353637383930" +
|
||||
"3132333435363738393031323334353637383930" +
|
||||
"3132333435363738393031323334353637383930" +
|
||||
"31323334";
|
||||
long T0 = 0;
|
||||
long X = 30;
|
||||
long testTime[] = {59L, 1111111109L, 1111111111L,
|
||||
1234567890L, 2000000000L, 20000000000L};
|
||||
|
||||
String steps = "0";
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 13]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
try {
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
System.out.println(
|
||||
"| Time(sec) | Time (UTC format) " +
|
||||
"| Value of T(Hex) | TOTP | Mode |");
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
|
||||
for (int i=0; i<testTime.length; i++) {
|
||||
long T = (testTime[i] - T0)/X;
|
||||
steps = Long.toHexString(T).toUpperCase();
|
||||
while (steps.length() < 16) steps = "0" + steps;
|
||||
String fmtTime = String.format("%1$-11s", testTime[i]);
|
||||
String utcTime = df.format(new Date(testTime[i]*1000));
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed, steps, "8",
|
||||
"HmacSHA1") + "| SHA1 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed32, steps, "8",
|
||||
"HmacSHA256") + "| SHA256 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed64, steps, "8",
|
||||
"HmacSHA512") + "| SHA512 |");
|
||||
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
}
|
||||
}catch (final Exception e){
|
||||
System.out.println("Error : " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<CODE ENDS>
|
||||
|
||||
Appendix B. Test Vectors
|
||||
|
||||
This section provides test values that can be used for the HOTP time-
|
||||
based variant algorithm interoperability test.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 14]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
The test token shared secret uses the ASCII string value
|
||||
"12345678901234567890". With Time Step X = 30, and the Unix epoch as
|
||||
the initial value to count time steps, where T0 = 0, the TOTP
|
||||
algorithm will display the following values for specified modes and
|
||||
timestamps.
|
||||
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
| Time (sec) | UTC Time | Value of T (hex) | TOTP | Mode |
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 94287082 | SHA1 |
|
||||
| | 00:00:59 | | | |
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 46119246 | SHA256 |
|
||||
| | 00:00:59 | | | |
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 90693936 | SHA512 |
|
||||
| | 00:00:59 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 07081804 | SHA1 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 68084774 | SHA256 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 25091201 | SHA512 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 14050471 | SHA1 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 67062674 | SHA256 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 99943326 | SHA512 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 89005924 | SHA1 |
|
||||
| | 23:31:30 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 91819424 | SHA256 |
|
||||
| | 23:31:30 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 93441116 | SHA512 |
|
||||
| | 23:31:30 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 69279037 | SHA1 |
|
||||
| | 03:33:20 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 90698825 | SHA256 |
|
||||
| | 03:33:20 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 38618901 | SHA512 |
|
||||
| | 03:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 65353130 | SHA1 |
|
||||
| | 11:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 77737706 | SHA256 |
|
||||
| | 11:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 47863826 | SHA512 |
|
||||
| | 11:33:20 | | | |
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
|
||||
Table 1: TOTP Table
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 15]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
David M'Raihi
|
||||
Verisign, Inc.
|
||||
685 E. Middlefield Road
|
||||
Mountain View, CA 94043
|
||||
USA
|
||||
|
||||
EMail: davidietf@gmail.com
|
||||
|
||||
|
||||
Salah Machani
|
||||
Diversinet Corp.
|
||||
2225 Sheppard Avenue East, Suite 1801
|
||||
Toronto, Ontario M2J 5C2
|
||||
Canada
|
||||
|
||||
EMail: smachani@diversinet.com
|
||||
|
||||
|
||||
Mingliang Pei
|
||||
Symantec
|
||||
510 E. Middlefield Road
|
||||
Mountain View, CA 94043
|
||||
USA
|
||||
|
||||
EMail: Mingliang_Pei@symantec.com
|
||||
|
||||
|
||||
Johan Rydell
|
||||
Portwise, Inc.
|
||||
275 Hawthorne Ave., Suite 119
|
||||
Palo Alto, CA 94301
|
||||
USA
|
||||
|
||||
EMail: johanietf@gmail.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 16]
|
||||
|
2131
doc/rfc/rfc6287.txt
2131
doc/rfc/rfc6287.txt
File diff suppressed because it is too large
Load diff
|
@ -10,12 +10,3 @@ security_HEADERS = \
|
|||
pam_constants.h \
|
||||
pam_modules.h \
|
||||
pam_types.h
|
||||
|
||||
if WITH_OATH
|
||||
security_HEADERS += \
|
||||
oath.h \
|
||||
oath_constants.h \
|
||||
oath_rfc3986.h \
|
||||
oath_rfc4648.h \
|
||||
oath_types.h
|
||||
endif
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2014 The University of Oslo
|
||||
* Copyright (c) 2013-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_H_INCLUDED
|
||||
#define OATH_H_INCLUDED
|
||||
|
||||
#include <security/oath_constants.h>
|
||||
#include <security/oath_types.h>
|
||||
#include <security/oath_rfc3986.h>
|
||||
#include <security/oath_rfc4648.h>
|
||||
|
||||
struct oath_key *oath_key_alloc(void);
|
||||
struct oath_key *oath_key_create(const char *, enum oath_mode,
|
||||
enum oath_hash, const char *, size_t);
|
||||
void oath_key_free(struct oath_key *);
|
||||
struct oath_key *oath_key_from_uri(const char *);
|
||||
struct oath_key *oath_key_from_file(const char *);
|
||||
char *oath_key_to_uri(const struct oath_key *);
|
||||
|
||||
struct oath_key *oath_key_dummy(enum oath_mode, enum oath_hash, unsigned int);
|
||||
|
||||
enum oath_mode oath_mode(const char *);
|
||||
|
||||
unsigned int oath_hotp(const uint8_t *, size_t, uint64_t, unsigned int);
|
||||
unsigned int oath_hotp_current(struct oath_key *);
|
||||
int oath_hotp_match(struct oath_key *, unsigned int, int);
|
||||
|
||||
unsigned int oath_totp(const uint8_t *, size_t, unsigned int);
|
||||
unsigned int oath_totp_current(const struct oath_key *);
|
||||
int oath_totp_match(struct oath_key *, unsigned int, int);
|
||||
|
||||
#endif
|
|
@ -1,84 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_CONSTANTS_H_INCLUDED
|
||||
#define OATH_CONSTANTS_H_INCLUDED
|
||||
|
||||
/*
|
||||
* OATH modes
|
||||
*/
|
||||
enum oath_mode {
|
||||
om_undef, /* not set / default */
|
||||
om_hotp, /* RFC 4226 HOTP */
|
||||
om_totp, /* RFC 6238 TOTP */
|
||||
om_max
|
||||
};
|
||||
|
||||
/*
|
||||
* Hash functions
|
||||
*/
|
||||
enum oath_hash {
|
||||
oh_undef, /* not set / default */
|
||||
oh_md5, /* RFC 1321 MD5 */
|
||||
oh_sha1, /* FIPS 180 SHA-1 */
|
||||
oh_sha256, /* FIPS 180 SHA-256 */
|
||||
oh_sha512, /* FIPS 180 SHA-512 */
|
||||
oh_max
|
||||
};
|
||||
|
||||
/*
|
||||
* Default time step for TOTP: 30 seconds.
|
||||
*/
|
||||
#define OATH_DEF_TIMESTEP 30
|
||||
|
||||
/*
|
||||
* Maximum time step for TOTP: 10 minutes, which RFC 6238 cites as an
|
||||
* example of an unreasonably large time step.
|
||||
*/
|
||||
#define OATH_MAX_TIMESTEP 600
|
||||
|
||||
/*
|
||||
* Maximum key length in bytes. HMAC has a 64-byte block size; if the key
|
||||
* K is longer than that, HMAC derives a new key K' = H(K).
|
||||
*/
|
||||
#define OATH_MAX_KEYLEN 64
|
||||
|
||||
/*
|
||||
* Maximum label length in characters, including terminating NUL.
|
||||
*/
|
||||
#define OATH_MAX_LABELLEN 64
|
||||
|
||||
/*
|
||||
* Label to use for dummy keys
|
||||
*/
|
||||
#define OATH_DUMMY_LABEL "oath-dummy@openpam.org"
|
||||
|
||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_RFC3986_H_INCLUDED
|
||||
#define OATH_RFC3986_H_INCLUDED
|
||||
|
||||
size_t oath_uri_decode(const char *, size_t, char *, size_t);
|
||||
|
||||
#endif
|
|
@ -1,51 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_RFC4648_H_INCLUDED
|
||||
#define OATH_RFC4648_H_INCLUDED
|
||||
|
||||
/* estimate of output length for base32 encoding / decoding */
|
||||
#define base32_enclen(l) (size_t)(((l + 4) / 5) * 8)
|
||||
#define base32_declen(l) (size_t)(((l + 7) / 8) * 5)
|
||||
|
||||
/* base32 encoding / decoding */
|
||||
int base32_enc(const char *, size_t, char *, size_t *);
|
||||
int base32_dec(const char *, size_t, char *, size_t *);
|
||||
|
||||
/* estimate of output length for base64 encoding / decoding */
|
||||
#define base64_enclen(l) (size_t)(((l + 2) / 3) * 4)
|
||||
#define base64_declen(l) (size_t)(((l + 3) / 4) * 3)
|
||||
|
||||
/* base64 encoding / decoding */
|
||||
int base64_enc(const char *, size_t, char *, size_t *);
|
||||
int base64_dec(const char *, size_t, char *, size_t *);
|
||||
|
||||
#endif
|
|
@ -1,63 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_TYPES_H_INCLUDED
|
||||
#define OATH_TYPES_H_INCLUDED
|
||||
|
||||
/*
|
||||
* OATH key and associated parameters
|
||||
*/
|
||||
struct oath_key {
|
||||
/* mode and parameters */
|
||||
enum oath_mode mode;
|
||||
unsigned int digits;
|
||||
uint64_t counter; /* HOTP only */
|
||||
unsigned int timestep; /* TOTP only - in seconds */
|
||||
uint64_t lastused; /* TOTP only */
|
||||
|
||||
/* housekeeping */
|
||||
unsigned int dummy:1; /* dummy key, always fail */
|
||||
unsigned int mapped:1; /* allocated with mmap() */
|
||||
unsigned int locked:1; /* locked / wired with madvise() */
|
||||
|
||||
/* hash algorithm */
|
||||
enum oath_hash hash;
|
||||
|
||||
/* label */
|
||||
size_t labellen; /* bytes incl. NUL */
|
||||
char label[OATH_MAX_LABELLEN];
|
||||
|
||||
/* key */
|
||||
size_t keylen; /* bytes */
|
||||
uint8_t key[OATH_MAX_KEYLEN];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -5,9 +5,3 @@ SUBDIRS =
|
|||
if !WITH_SYSTEM_LIBPAM
|
||||
SUBDIRS += libpam
|
||||
endif
|
||||
|
||||
if WITH_OATH
|
||||
if !WITH_SYSTEM_LIBOATH
|
||||
SUBDIRS += liboath
|
||||
endif
|
||||
endif
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
||||
|
||||
lib_LTLIBRARIES = liboath.la
|
||||
|
||||
noinst_HEADERS = oath_impl.h
|
||||
|
||||
liboath_la_SOURCES = \
|
||||
oath_base32.c \
|
||||
oath_base64.c \
|
||||
oath_hotp.c \
|
||||
oath_totp.c \
|
||||
oath_key_alloc.c \
|
||||
oath_key_create.c \
|
||||
oath_key_dummy.c \
|
||||
oath_key_from_file.c \
|
||||
oath_key_from_uri.c \
|
||||
oath_key_free.c \
|
||||
oath_key.c \
|
||||
oath_mode.c \
|
||||
oath_uri_decode.c
|
||||
|
||||
liboath_la_LDFLAGS = -no-undefined -version-info $(LIB_MAJ)
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
liboath_la_LIBADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
liboath_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
liboath_la_LIBADD += $(CRYPTO_LIBS)
|
|
@ -1,205 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#include "oath_impl.h"
|
||||
|
||||
static const char b32enc[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
static const char b32dec[256] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
/*
|
||||
* Encode data in RFC 4648 base 32 representation. The target buffer must
|
||||
* have room for base32_enclen(len) characters and a terminating NUL.
|
||||
*/
|
||||
int
|
||||
base32_enc(const char *cin, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t *)cin;
|
||||
uint64_t bits;
|
||||
|
||||
if (*olen <= base32_enclen(ilen)) {
|
||||
*olen = base32_enclen(ilen) + 1;
|
||||
errno = ENOSPC;
|
||||
return (-1);
|
||||
}
|
||||
*olen = 0;
|
||||
while (ilen >= 5) {
|
||||
bits = 0;
|
||||
bits |= (uint64_t)in[0] << 32;
|
||||
bits |= (uint64_t)in[1] << 24;
|
||||
bits |= (uint64_t)in[2] << 16;
|
||||
bits |= (uint64_t)in[3] << 8;
|
||||
bits |= (uint64_t)in[4];
|
||||
ilen -= 5;
|
||||
in += 5;
|
||||
out[0] = b32enc[bits >> 35 & 0x1f];
|
||||
out[1] = b32enc[bits >> 30 & 0x1f];
|
||||
out[2] = b32enc[bits >> 25 & 0x1f];
|
||||
out[3] = b32enc[bits >> 20 & 0x1f];
|
||||
out[4] = b32enc[bits >> 15 & 0x1f];
|
||||
out[5] = b32enc[bits >> 10 & 0x1f];
|
||||
out[6] = b32enc[bits >> 5 & 0x1f];
|
||||
out[7] = b32enc[bits & 0x1f];
|
||||
*olen += 8;
|
||||
out += 8;
|
||||
}
|
||||
if (ilen > 0) {
|
||||
bits = 0;
|
||||
switch (ilen) {
|
||||
case 4:
|
||||
bits |= (uint64_t)in[3] << 8;
|
||||
case 3:
|
||||
bits |= (uint64_t)in[2] << 16;
|
||||
case 2:
|
||||
bits |= (uint64_t)in[1] << 24;
|
||||
case 1:
|
||||
bits |= (uint64_t)in[0] << 32;
|
||||
COVERAGE_NO_DEFAULT_CASE
|
||||
}
|
||||
out[0] = b32enc[bits >> 35 & 0x1f];
|
||||
out[1] = b32enc[bits >> 30 & 0x1f];
|
||||
out[2] = ilen > 1 ? b32enc[bits >> 25 & 0x1f] : '=';
|
||||
out[3] = ilen > 1 ? b32enc[bits >> 20 & 0x1f] : '=';
|
||||
out[4] = ilen > 2 ? b32enc[bits >> 15 & 0x1f] : '=';
|
||||
out[5] = ilen > 3 ? b32enc[bits >> 10 & 0x1f] : '=';
|
||||
out[6] = ilen > 3 ? b32enc[bits >> 5 & 0x1f] : '=';
|
||||
out[7] = '=';
|
||||
*olen += 8;
|
||||
out += 8;
|
||||
}
|
||||
out[0] = '\0';
|
||||
++*olen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode data in RFC 4648 base 32 representation, stopping at the
|
||||
* terminating NUL, the first invalid (non-base32, non-whitespace)
|
||||
* character or after len characters, whichever comes first.
|
||||
*
|
||||
* Padding is handled sloppily: any padding character following the data
|
||||
* is silently consumed. This not only simplifies the code but ensures
|
||||
* compatibility with implementations which do not emit or understand
|
||||
* padding.
|
||||
*
|
||||
* The olen argument is used by the caller to pass the size of the buffer
|
||||
* and by base32_dec() to return the amount of data successfully decoded.
|
||||
* If the buffer is too small, base32_dec() discards the excess data, but
|
||||
* returns the total amount.
|
||||
*/
|
||||
int
|
||||
base32_dec(const char *cin, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t *)cin;
|
||||
size_t len;
|
||||
int bits, shift, padding;
|
||||
|
||||
for (bits = shift = padding = len = 0; ilen && *in; --ilen, ++in) {
|
||||
if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n' ||
|
||||
(padding && *in == '=')) {
|
||||
/* consume */
|
||||
continue;
|
||||
} else if (!padding && b32dec[*in] >= 0) {
|
||||
/* shift into accumulator */
|
||||
shift += 5;
|
||||
bits = bits << 5 | b32dec[*in];
|
||||
} else if (!padding && shift > 0 && shift < 5 && *in == '=') {
|
||||
/* final byte */
|
||||
shift = 0;
|
||||
padding = 1;
|
||||
} else {
|
||||
/* error */
|
||||
*olen = 0;
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (shift >= 8) {
|
||||
/* output accumulated byte */
|
||||
shift -= 8;
|
||||
if (len++ < *olen)
|
||||
*out++ = (bits >> shift) & 0xff;
|
||||
}
|
||||
}
|
||||
/* report decoded length */
|
||||
if (len > *olen) {
|
||||
/* overflow */
|
||||
*olen = len;
|
||||
errno = ENOSPC;
|
||||
return (-1);
|
||||
}
|
||||
*olen = len;
|
||||
return (0);
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#include "oath_impl.h"
|
||||
|
||||
static const char b64enc[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static const char b64dec[256] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
/*
|
||||
* Encode data in RFC 4648 base 64 representation. The target buffer must
|
||||
* have room for base64_enclen(len) characters and a terminating NUL.
|
||||
*/
|
||||
int
|
||||
base64_enc(const char *cin, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t *)cin;
|
||||
uint32_t bits;
|
||||
|
||||
if (*olen <= base64_enclen(ilen)) {
|
||||
*olen = base64_enclen(ilen) + 1;
|
||||
errno = ENOSPC;
|
||||
return (-1);
|
||||
}
|
||||
*olen = 0;
|
||||
while (ilen >= 3) {
|
||||
bits = 0;
|
||||
bits |= (uint32_t)in[0] << 16;
|
||||
bits |= (uint32_t)in[1] << 8;
|
||||
bits |= (uint32_t)in[2];
|
||||
ilen -= 3;
|
||||
in += 3;
|
||||
out[0] = b64enc[bits >> 18 & 0x3f];
|
||||
out[1] = b64enc[bits >> 12 & 0x3f];
|
||||
out[2] = b64enc[bits >> 6 & 0x3f];
|
||||
out[3] = b64enc[bits & 0x3f];
|
||||
*olen += 4;
|
||||
out += 4;
|
||||
}
|
||||
if (ilen > 0) {
|
||||
bits = 0;
|
||||
switch (ilen) {
|
||||
case 2:
|
||||
bits |= (uint32_t)in[1] << 8;
|
||||
case 1:
|
||||
bits |= (uint32_t)in[0] << 16;
|
||||
COVERAGE_NO_DEFAULT_CASE
|
||||
}
|
||||
out[0] = b64enc[bits >> 18 & 0x3f];
|
||||
out[1] = b64enc[bits >> 12 & 0x3f];
|
||||
out[2] = ilen > 1 ? b64enc[bits >> 6 & 0x3f] : '=';
|
||||
out[3] = '=';
|
||||
*olen += 4;
|
||||
out += 4;
|
||||
}
|
||||
out[0] = '\0';
|
||||
++*olen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode data in RFC 4648 base 64 representation, stopping at the
|
||||
* terminating NUL, the first invalid (non-base64, non-whitespace)
|
||||
* character or after len characters, whichever comes first.
|
||||
*
|
||||
* Padding is handled sloppily: any padding character following the data
|
||||
* is silently consumed. This not only simplifies the code but ensures
|
||||
* compatibility with implementations which do not emit or understand
|
||||
* padding.
|
||||
*
|
||||
* The olen argument is used by the caller to pass the size of the buffer
|
||||
* and by base64_dec() to return the amount of data successfully decoded.
|
||||
* If the buffer is too small, base64_dec() discards the excess data, but
|
||||
* returns the total amount.
|
||||
*/
|
||||
int
|
||||
base64_dec(const char *cin, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t *)cin;
|
||||
size_t len;
|
||||
int bits, shift, padding;
|
||||
|
||||
for (bits = shift = padding = len = 0; ilen && *in; --ilen, ++in) {
|
||||
if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n' ||
|
||||
(padding && *in == '=')) {
|
||||
/* consume */
|
||||
continue;
|
||||
} else if (!padding && b64dec[*in] >= 0) {
|
||||
/* shift into accumulator */
|
||||
shift += 6;
|
||||
bits = bits << 6 | b64dec[*in];
|
||||
} else if (!padding && shift > 0 && shift != 6 && *in == '=') {
|
||||
/* final byte */
|
||||
shift = 0;
|
||||
padding = 1;
|
||||
} else {
|
||||
/* error */
|
||||
*olen = 0;
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (shift >= 8) {
|
||||
/* output accumulated byte */
|
||||
shift -= 8;
|
||||
if (len++ < *olen)
|
||||
*out++ = (bits >> shift) & 0xff;
|
||||
}
|
||||
}
|
||||
/* report decoded length */
|
||||
if (len > *olen) {
|
||||
/* overflow */
|
||||
*olen = len;
|
||||
errno = ENOSPC;
|
||||
return (-1);
|
||||
}
|
||||
*olen = len;
|
||||
return (0);
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#define StToNum(St) (St)
|
||||
|
||||
static uint32_t
|
||||
DT(const uint8_t *String)
|
||||
{
|
||||
uint8_t OffsetBits;
|
||||
int Offset;
|
||||
uint32_t P;
|
||||
|
||||
OffsetBits = String[19] & 0x0f;
|
||||
Offset = StToNum(OffsetBits);
|
||||
P = (uint32_t)String[Offset + 0] << 24 |
|
||||
(uint32_t)String[Offset + 1] << 16 |
|
||||
(uint32_t)String[Offset + 2] << 8 |
|
||||
(uint32_t)String[Offset + 3];
|
||||
return (P & 0x7fffffffUL);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
oath_hotp(const uint8_t *K, size_t Klen, uint64_t seq, unsigned int Digit)
|
||||
{
|
||||
HMAC_CTX ctx;
|
||||
uint8_t C[8];
|
||||
uint8_t HS[20];
|
||||
unsigned int HSlen;
|
||||
uint32_t Sbits, Snum;
|
||||
unsigned int mod, D;
|
||||
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
C[i] = seq & 0xff;
|
||||
seq >>= 8;
|
||||
}
|
||||
|
||||
/* HS = HMAC-SHA-1(K,C) */
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init_ex(&ctx, K, Klen, EVP_sha1(), NULL);
|
||||
HMAC_Update(&ctx, (const uint8_t *)&C, sizeof C);
|
||||
HMAC_Final(&ctx, HS, &HSlen);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
Sbits = DT(HS);
|
||||
Snum = StToNum(Sbits);
|
||||
for (mod = 1; Digit > 0; --Digit)
|
||||
mod *= 10;
|
||||
D = Snum % mod;
|
||||
return (D);
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes the current code for the given key and advances the counter.
|
||||
*/
|
||||
unsigned int
|
||||
oath_hotp_current(struct oath_key *k)
|
||||
{
|
||||
unsigned int code;
|
||||
|
||||
if (k == NULL)
|
||||
return (UINT_MAX);
|
||||
if (k->mode != om_hotp)
|
||||
return (UINT_MAX);
|
||||
if (k->counter == UINT64_MAX)
|
||||
return (UINT_MAX);
|
||||
code = oath_hotp(k->key, k->keylen, k->counter, k->digits);
|
||||
k->counter += 1;
|
||||
return (code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares the code provided by the user with expected values within a
|
||||
* given window. Returns 1 if there was a match, 0 if not, and -1 if an
|
||||
* error occurred. Also advances the counter if there was a match.
|
||||
*/
|
||||
int
|
||||
oath_hotp_match(struct oath_key *k, unsigned int response, int window)
|
||||
{
|
||||
unsigned int code;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (window < 0)
|
||||
return (-1);
|
||||
if (k->mode != om_hotp)
|
||||
return (-1);
|
||||
if (k->counter >= UINT64_MAX - window - 1)
|
||||
return (-1);
|
||||
for (int i = 0; i <= window; ++i) {
|
||||
code = oath_hotp(k->key, k->keylen, k->counter + i, k->digits);
|
||||
if (code == response && !k->dummy) {
|
||||
k->counter = k->counter + i + 1;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 The University of Oslo
|
||||
* Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_IMPL_H_INCLUDED
|
||||
#define OATH_IMPL_H_INCLUDED
|
||||
|
||||
#if _BullseyeCoverage
|
||||
#define COVERAGE_DISABLE _Pragma("BullseyeCoverage save off")
|
||||
#define COVERAGE_RESTORE _Pragma("BullseyeCoverage restore")
|
||||
#else
|
||||
#define COVERAGE_DISABLE
|
||||
#define COVERAGE_RESTORE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use at end of switch which has no default case
|
||||
*/
|
||||
#if _BullseyeCoverage
|
||||
#define COVERAGE_NO_DEFAULT_CASE \
|
||||
COVERAGE_DISABLE \
|
||||
default: \
|
||||
(void)0; \
|
||||
COVERAGE_RESTORE
|
||||
#else
|
||||
#define COVERAGE_NO_DEFAULT_CASE
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,102 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2016 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_asprintf.h"
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
char *
|
||||
oath_key_to_uri(const struct oath_key *key)
|
||||
{
|
||||
const char *hash;
|
||||
char *tmp, *uri;
|
||||
size_t kslen, urilen;
|
||||
|
||||
switch (key->hash) {
|
||||
case oh_sha1:
|
||||
hash = "SHA1";
|
||||
break;
|
||||
case oh_sha256:
|
||||
hash = "SHA256";
|
||||
break;
|
||||
case oh_sha512:
|
||||
hash = "SHA512";
|
||||
break;
|
||||
case oh_md5:
|
||||
hash = "MD5";
|
||||
break;
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* XXX the label and secret should be URI-encoded */
|
||||
if (key->mode == om_hotp) {
|
||||
urilen = asprintf(&uri, "otpauth://%s/%s?"
|
||||
"algorithm=%s&digits=%d&counter=%ju&secret=",
|
||||
"hotp", key->label, hash, key->digits,
|
||||
(uintmax_t)key->counter);
|
||||
} else if (key->mode == om_totp) {
|
||||
urilen = asprintf(&uri, "otpauth://%s/%s?"
|
||||
"algorithm=%s&digits=%d&period=%u&lastused=%ju&secret=",
|
||||
"totp", key->label, hash, key->digits, key->timestep,
|
||||
(uintmax_t)key->lastused);
|
||||
} else {
|
||||
/* unreachable */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* compute length of base32-encoded key and append it */
|
||||
kslen = base32_enclen(key->keylen) + 1;
|
||||
if ((tmp = realloc(uri, urilen + kslen)) == NULL) {
|
||||
free(uri);
|
||||
return (NULL);
|
||||
}
|
||||
uri = tmp;
|
||||
if (base32_enc((const char *)key->key, key->keylen, uri + urilen, &kslen) != 0) {
|
||||
free(uri);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (uri);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Allocates an OATH key structure
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_alloc(void)
|
||||
{
|
||||
struct oath_key *key;
|
||||
int prot, flags;
|
||||
|
||||
prot = PROT_READ|PROT_WRITE;
|
||||
flags = MAP_ANON;
|
||||
#ifdef MAP_NOCORE
|
||||
flags |= MAP_NOCORE;
|
||||
#endif
|
||||
if ((key = mmap(NULL, sizeof *key, prot, flags, -1, 0)) != MAP_FAILED) {
|
||||
memset(key, 0, sizeof *key);
|
||||
key->mapped = 1;
|
||||
if (mlock(key, sizeof *key) == 0)
|
||||
key->locked = 1;
|
||||
} else {
|
||||
openpam_log(PAM_LOG_ERROR, "mmap(): %m");
|
||||
if ((key = calloc(sizeof *key, 1)) == NULL)
|
||||
openpam_log(PAM_LOG_ERROR, "malloc(): %m");
|
||||
}
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_alloc function allocates and initializes an OATH key
|
||||
* structure.
|
||||
*
|
||||
* Keys allocated with =oath_key_alloc must be freed using =oath_key_free.
|
||||
*
|
||||
* >oath_key_free
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,178 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2014 The University of Oslo
|
||||
* Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Creates an OATH key with the specified parameters
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_create(const char *label,
|
||||
enum oath_mode mode, enum oath_hash hash,
|
||||
const char *keydata, size_t keylen)
|
||||
{
|
||||
char keybuf[OATH_MAX_KEYLEN];
|
||||
struct oath_key *key;
|
||||
int labellen;
|
||||
|
||||
/* check label length */
|
||||
if (label == NULL ||
|
||||
(labellen = strlen(label)) >= OATH_MAX_LABELLEN)
|
||||
return (NULL);
|
||||
|
||||
/* check key length */
|
||||
if (keylen > OATH_MAX_KEYLEN ||
|
||||
(keydata != NULL && keylen == 0))
|
||||
return (NULL);
|
||||
if (keylen == 0)
|
||||
keylen = 20;
|
||||
|
||||
/* check mode */
|
||||
switch (mode) {
|
||||
case om_hotp:
|
||||
case om_totp:
|
||||
break;
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* check hash */
|
||||
switch (hash) {
|
||||
case oh_undef:
|
||||
hash = oh_sha1;
|
||||
break;
|
||||
case oh_md5:
|
||||
case oh_sha1:
|
||||
case oh_sha256:
|
||||
case oh_sha512:
|
||||
break;
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* generate key data if necessary */
|
||||
if (keydata == NULL) {
|
||||
if (RAND_bytes((void *)keybuf, keylen) != 1)
|
||||
return (NULL);
|
||||
keydata = keybuf;
|
||||
}
|
||||
|
||||
/* allocate */
|
||||
if ((key = oath_key_alloc()) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* label */
|
||||
memcpy(key->label, label, labellen);
|
||||
key->label[labellen] = 0;
|
||||
key->labellen = labellen;
|
||||
|
||||
/* mode and hash */
|
||||
key->mode = mode;
|
||||
key->hash = hash;
|
||||
|
||||
/* default parameters */
|
||||
key->digits = 6;
|
||||
if (key->mode == om_totp)
|
||||
key->timestep = 30;
|
||||
|
||||
/* key */
|
||||
memcpy(key->key, keydata, keylen);
|
||||
key->keylen = keylen;
|
||||
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_create function allocates and initializes an OATH key
|
||||
* structure with the specified parameters.
|
||||
*
|
||||
* The =label parameter must point to a string describing the key.
|
||||
*
|
||||
* The =mode parameter indicates the OTP algorithm to use:
|
||||
*
|
||||
* ;om_hotp:
|
||||
* RFC 4226 HOTP
|
||||
* ;om_totp:
|
||||
* RFC 6238 TOTP
|
||||
*
|
||||
* The =hash parameter indicates which hash algorithm to use:
|
||||
*
|
||||
* ;oh_md5:
|
||||
* RFC 1321 MD5
|
||||
* ;oh_sha1:
|
||||
* RFC 3174 SHA-1
|
||||
* ;oh_sha256:
|
||||
* RFC 6234 SHA-256
|
||||
* ;oh_sha512:
|
||||
* RFC 6234 SHA-512
|
||||
*
|
||||
* If =hash is ;oh_undef, the default algorithm (SHA-1) is used.
|
||||
*
|
||||
* The =keydata parameter should point to a buffer containing the raw key
|
||||
* to use.
|
||||
* If =keydata is NULL, a key will be randomly generated.
|
||||
* Note that the strength of the generated key is dependent on the
|
||||
* strength of the operating system's pseudo-random number generator.
|
||||
*
|
||||
* The =keylen parameter specifies the length of the provided (or
|
||||
* generated) key in bytes.
|
||||
* Note that some OATH HOTP / TOTP implementations do not support key
|
||||
* lengths that are not a multiple of 20 bits (5 bytes).
|
||||
* If =keydata is NULL and =keylen is 0, a hardcoded default of 160 bits
|
||||
* (20 bytes) is used.
|
||||
*
|
||||
* The following key parameters are set to hardcoded default values and
|
||||
* can be changed after key creation:
|
||||
*
|
||||
* - For HOTP keys, the initial counter value is set to 0.
|
||||
* - For TOTP keys, the timestep is set to 30 seconds.
|
||||
* - For both HOTP and TOTP keys, the number of digits is set to 6.
|
||||
*
|
||||
* Keys created with =oath_key_create must be freed using =oath_key_free.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
* >oath_key_free
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,77 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Creates a dummy OATH key structure
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_dummy(enum oath_mode mode, enum oath_hash hash, unsigned int digits)
|
||||
{
|
||||
struct oath_key *key;
|
||||
|
||||
if ((key = oath_key_alloc()) == NULL)
|
||||
return (NULL);
|
||||
key->dummy = 1;
|
||||
key->mode = mode;
|
||||
key->digits = digits;
|
||||
key->counter = 0;
|
||||
key->timestep = 30;
|
||||
key->hash = hash;
|
||||
memcpy(key->label, OATH_DUMMY_LABEL, sizeof OATH_DUMMY_LABEL);
|
||||
key->labellen = sizeof OATH_DUMMY_LABEL - 1;
|
||||
key->keylen = sizeof key->key;
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_dummy function allocates and initializes a dummy OATH key
|
||||
* structure.
|
||||
* Authentication attempts using a dummy key will always fail.
|
||||
*
|
||||
* Keys allocated with =oath_key_dummy must be freed using =oath_key_free.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
* >oath_key_free
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,78 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Wipes and frees an OATH key structure
|
||||
*/
|
||||
|
||||
void
|
||||
oath_key_free(struct oath_key *key)
|
||||
{
|
||||
int mapped, locked;
|
||||
|
||||
if (key != NULL) {
|
||||
mapped = key->mapped;
|
||||
locked = key->locked;
|
||||
memset(key, 0, sizeof *key);
|
||||
if (mapped) {
|
||||
if (locked)
|
||||
munlock(key, sizeof *key);
|
||||
munmap(key, sizeof *key);
|
||||
} else {
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_free function wipes and frees an OATH key structure which
|
||||
* was previously allocated using the =oath_key_alloc function.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,96 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 The University of Oslo
|
||||
* Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_strlcmp.h"
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Loads an OATH key from a file
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_from_file(const char *filename)
|
||||
{
|
||||
struct oath_key *key;
|
||||
FILE *f;
|
||||
char *line;
|
||||
size_t len;
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL)
|
||||
return (NULL);
|
||||
/* get first non-empty non-comment line */
|
||||
line = openpam_readline(f, NULL, &len);
|
||||
fclose(f);
|
||||
if (strlcmp("otpauth://", line, len) == 0) {
|
||||
key = oath_key_from_uri(line);
|
||||
} else {
|
||||
openpam_log(PAM_LOG_ERROR,
|
||||
"unrecognized key file format: %s", filename);
|
||||
key = NULL;
|
||||
}
|
||||
free(line);
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_from_file function loads a key from the specified file.
|
||||
* The file format is automatically detected.
|
||||
*
|
||||
* The following key file formats are supported:
|
||||
*
|
||||
* - otpauth URI
|
||||
*
|
||||
* Keys created with =oath_key_from_file must be freed using
|
||||
* =oath_key_free.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
* >oath_key_free
|
||||
* >oath_key_from_uri
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,234 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2016 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_strlcmp.h"
|
||||
#include "openpam_strlcpy.h"
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Creates an OATH key from a Google otpauth URI
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_from_uri(const char *uri)
|
||||
{
|
||||
char name[64], value[256];
|
||||
size_t namelen, valuelen;
|
||||
struct oath_key *key;
|
||||
const char *p, *q, *r;
|
||||
uintmax_t n;
|
||||
char *e;
|
||||
|
||||
if ((key = oath_key_alloc()) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* check method */
|
||||
p = uri;
|
||||
if (strlcmp("otpauth://", p, 10) != 0)
|
||||
goto invalid;
|
||||
p += 10;
|
||||
|
||||
/* check mode (hotp = event, totp = time-sync) */
|
||||
if ((q = strchr(p, '/')) == NULL)
|
||||
goto invalid;
|
||||
if (strlcmp("hotp", p, q - p) == 0) {
|
||||
key->mode = om_hotp;
|
||||
} else if (strlcmp("totp", p, q - p) == 0) {
|
||||
key->mode = om_totp;
|
||||
} else {
|
||||
goto invalid;
|
||||
}
|
||||
p = q + 1;
|
||||
|
||||
/* extract label */
|
||||
if ((q = strchr(p, '?')) == NULL)
|
||||
goto invalid;
|
||||
valuelen = oath_uri_decode(p, q - p, value, sizeof value) - 1;
|
||||
key->labellen = strlcpy(key->label, value, sizeof key->label);
|
||||
if (key->labellen >= sizeof key->label)
|
||||
goto invalid;
|
||||
p = q + 1;
|
||||
|
||||
/* extract parameters */
|
||||
key->counter = UINT64_MAX;
|
||||
key->lastused = UINT64_MAX;
|
||||
while (*p != '\0') {
|
||||
/* locate name-value separator */
|
||||
if ((q = strchr(p, '=')) == NULL)
|
||||
goto invalid;
|
||||
q = q + 1;
|
||||
/* locate end of value */
|
||||
if ((r = strchr(p, '&')) == NULL)
|
||||
r = strchr(p, '\0');
|
||||
if (r < q)
|
||||
/* & before = */
|
||||
goto invalid;
|
||||
/* decode name and value*/
|
||||
namelen = oath_uri_decode(p, q - p - 1, name, sizeof name) - 1;
|
||||
if (namelen >= sizeof name)
|
||||
goto invalid;
|
||||
valuelen = oath_uri_decode(q, r - q, value, sizeof value) - 1;
|
||||
if (valuelen >= sizeof value)
|
||||
goto invalid;
|
||||
if (strcmp("secret", name) == 0) {
|
||||
if (key->keylen != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
key->keylen = sizeof key->key;
|
||||
if (base32_dec(value, valuelen, (char *)key->key, &key->keylen) != 0)
|
||||
goto invalid;
|
||||
} else if (strcmp("algorithm", name) == 0) {
|
||||
if (key->hash != oh_undef)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
if (strcmp("SHA1", value) == 0)
|
||||
key->hash = oh_sha1;
|
||||
else if (strcmp("SHA256", value) == 0)
|
||||
key->hash = oh_sha256;
|
||||
else if (strcmp("SHA512", value) == 0)
|
||||
key->hash = oh_sha512;
|
||||
else if (strcmp("MD5", value) == 0)
|
||||
key->hash = oh_md5;
|
||||
else
|
||||
goto invalid;
|
||||
} else if (strcmp("digits", name) == 0) {
|
||||
if (key->digits != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
/* only 6 or 8 */
|
||||
if (valuelen != 1 || (*value != '6' && *value != '8'))
|
||||
goto invalid;
|
||||
key->digits = *q - '0';
|
||||
} else if (strcmp("counter", name) == 0) {
|
||||
if (key->counter != UINT64_MAX)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
n = strtoumax(value, &e, 10);
|
||||
if (e == value || *e != '\0' || n >= UINT64_MAX)
|
||||
goto invalid;
|
||||
key->counter = (uint64_t)n;
|
||||
} else if (strcmp("lastused", name) == 0) {
|
||||
if (key->lastused != UINT64_MAX)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
n = strtoumax(value, &e, 10);
|
||||
if (e == value || *e != '\0' || n >= UINT64_MAX)
|
||||
goto invalid;
|
||||
key->lastused = (uint64_t)n;
|
||||
} else if (strcmp("period", name) == 0) {
|
||||
if (key->timestep != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
n = strtoumax(value, &e, 10);
|
||||
if (e == value || *e != '\0' || n > OATH_MAX_TIMESTEP)
|
||||
goto invalid;
|
||||
key->timestep = n;
|
||||
} else if (strcmp("issuer", name) == 0) {
|
||||
// noop for now
|
||||
} else {
|
||||
goto invalid;
|
||||
}
|
||||
/* final parameter? */
|
||||
if (*r == '\0')
|
||||
break;
|
||||
/* skip & and continue */
|
||||
p = r + 1;
|
||||
}
|
||||
|
||||
/* sanity checks and default values */
|
||||
if (key->mode == om_hotp) {
|
||||
if (key->counter == UINT64_MAX)
|
||||
key->counter = 0;
|
||||
if (key->timestep != 0)
|
||||
goto invalid;
|
||||
if (key->lastused != UINT64_MAX)
|
||||
goto invalid;
|
||||
} else if (key->mode == om_totp) {
|
||||
if (key->counter != UINT64_MAX)
|
||||
goto invalid;
|
||||
if (key->timestep == 0)
|
||||
key->timestep = OATH_DEF_TIMESTEP;
|
||||
if (key->lastused == UINT64_MAX)
|
||||
key->lastused = 0;
|
||||
} else {
|
||||
/* unreachable */
|
||||
oath_key_free(key);
|
||||
return (NULL);
|
||||
}
|
||||
if (key->hash == oh_undef)
|
||||
key->hash = oh_sha1;
|
||||
if (key->digits == 0)
|
||||
key->digits = 6;
|
||||
if (key->keylen == 0)
|
||||
goto invalid;
|
||||
return (key);
|
||||
|
||||
invalid:
|
||||
openpam_log(PAM_LOG_NOTICE, "invalid OATH URI: %s", uri);
|
||||
oath_key_free(key);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_from_uri function parses a Google otpauth URI into a key
|
||||
* structure.
|
||||
*
|
||||
* The =uri parameter points to a NUL-terminated string containing the
|
||||
* URI.
|
||||
*
|
||||
* Keys created with =oath_key_from_uri must be freed using
|
||||
* =oath_key_free.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
* >oath_key_free
|
||||
* >oath_key_to_uri
|
||||
*
|
||||
* REFERENCES
|
||||
*
|
||||
* https://code.google.com/p/google-authenticator/wiki/KeyUriFormat
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,72 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
static const char *oath_mode_names[om_max] = {
|
||||
[om_hotp] = "hotp",
|
||||
[om_totp] = "totp",
|
||||
};
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Converts a mode name to the corresponding enum value
|
||||
*/
|
||||
|
||||
enum oath_mode
|
||||
oath_mode(const char *str)
|
||||
{
|
||||
enum oath_mode om;
|
||||
|
||||
for (om = 0; om < om_max; ++om) {
|
||||
if (oath_mode_names[om] != NULL &&
|
||||
strcasecmp(oath_mode_names[om], str) == 0) {
|
||||
return (om);
|
||||
}
|
||||
}
|
||||
return (om_undef);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_mode function returns the =enum oath_mode value that
|
||||
* corresponds to the specified string.
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,101 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2014 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#define TOTP_TIME_STEP 30
|
||||
|
||||
unsigned int
|
||||
oath_totp(const uint8_t *K, size_t Klen, unsigned int Digit)
|
||||
{
|
||||
time_t now;
|
||||
|
||||
time(&now);
|
||||
return (oath_hotp(K, Klen, now / TOTP_TIME_STEP, Digit));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
oath_totp_current(const struct oath_key *k)
|
||||
{
|
||||
unsigned int code;
|
||||
uint64_t seq;
|
||||
|
||||
if (k == NULL)
|
||||
return (UINT_MAX);
|
||||
if (k->mode != om_totp)
|
||||
return (UINT_MAX);
|
||||
if (k->timestep == 0)
|
||||
return (UINT_MAX);
|
||||
seq = time(NULL) / k->timestep;
|
||||
code = oath_hotp(k->key, k->keylen, seq, k->digits);
|
||||
return (code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares the code provided by the user with expected values within a
|
||||
* given window. Returns 1 if there was a match, 0 if not, and -1 if an
|
||||
* error occurred.
|
||||
*/
|
||||
int
|
||||
oath_totp_match(struct oath_key *k, unsigned int response, int window)
|
||||
{
|
||||
unsigned int code;
|
||||
uint64_t seq;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (window < 0)
|
||||
return (-1);
|
||||
if (k->mode != om_totp)
|
||||
return (-1);
|
||||
if (k->timestep == 0)
|
||||
return (-1);
|
||||
seq = time(NULL) / k->timestep;
|
||||
for (int i = -window; i <= window; ++i) {
|
||||
if (seq + i <= k->lastused)
|
||||
continue;
|
||||
code = oath_hotp(k->key, k->keylen, seq + i, k->digits);
|
||||
if (code == response && !k->dummy) {
|
||||
k->lastused = seq;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2014-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "openpam_ctype.h"
|
||||
|
||||
#define unhex(ch) \
|
||||
((ch >= '0' && ch <= '9') ? ch - '0' : \
|
||||
(ch >= 'A' && ch <= 'F') ? 0xa + ch - 'A' : \
|
||||
(ch >= 'a' && ch <= 'f') ? 0xa + ch - 'a' : 0)
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Decodes a URI-encoded string.
|
||||
*/
|
||||
|
||||
size_t
|
||||
oath_uri_decode(const char *in, size_t ilen, char *out, size_t olen)
|
||||
{
|
||||
size_t ipos, opos;
|
||||
|
||||
if (ilen == 0)
|
||||
ilen = strlen(in);
|
||||
for (ipos = opos = 0; ipos < ilen && in[ipos] != '\0'; ++ipos, ++opos) {
|
||||
if (in[ipos] == '%' && ipos + 2 < ilen &&
|
||||
is_xdigit(in[ipos + 1]) && is_xdigit(in[ipos + 2])) {
|
||||
if (out != NULL && opos < olen - 1)
|
||||
out[opos] = unhex(in[ipos + 1]) * 16 +
|
||||
unhex(in[ipos + 2]);
|
||||
ipos += 2;
|
||||
} else {
|
||||
if (out != NULL && opos < olen - 1)
|
||||
out[opos] = in[ipos];
|
||||
}
|
||||
}
|
||||
if (out != NULL && olen > 0)
|
||||
out[opos < olen ? opos : olen - 1] = '\0';
|
||||
return (opos + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_uri_decode function decodes a URI-encoded ("percent-encoded")
|
||||
* string.
|
||||
*
|
||||
* The =in parameter points to the string to be decoded, and the =ilen
|
||||
* parameter is its length. If =ilen is 0, =oath_uri_decode will decode
|
||||
* the entire string.
|
||||
*
|
||||
* The =out parameter points to a buffer in which the decoded data is to
|
||||
* be stored; the =olen parameter is the size of that buffer. If =out is
|
||||
* =NULL, the decoded data is discarded, but =oath_uri_decode still counts
|
||||
* the amount of space needed to store it.
|
||||
*
|
||||
* The output buffer is always NUL-terminated, regardless of how much or
|
||||
* how little data was decoded.
|
||||
*
|
||||
* RETURN VALUES
|
||||
*
|
||||
* The =oath_uri_decode funtion always returns the amount of space
|
||||
* required to store the entire decoded string, including the terminating
|
||||
* NUL. This may exceed the actual size of the output buffer.
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -347,9 +347,6 @@ sub parse_source($) {
|
|||
if ($source !~ m/^ \* XSSO \d/m) {
|
||||
$FUNCTIONS{$func}->{openpam} = 1;
|
||||
}
|
||||
if ($func =~ m/^oath/) {
|
||||
$FUNCTIONS{$func}->{oath} = 1;
|
||||
}
|
||||
expand_errors($FUNCTIONS{$func});
|
||||
return $FUNCTIONS{$func};
|
||||
}
|
||||
|
@ -453,25 +450,14 @@ sub gendoc($) {
|
|||
if ($func =~ m/^(?:open)?pam_/) {
|
||||
$mdoc .= ".Sh LIBRARY
|
||||
.Lb libpam
|
||||
";
|
||||
} elsif ($func =~ m/^oath_/) {
|
||||
$mdoc .= ".Sh LIBRARY
|
||||
.Lb liboath
|
||||
";
|
||||
}
|
||||
$mdoc .= ".Sh SYNOPSIS
|
||||
.In sys/types.h
|
||||
";
|
||||
if ($$func{name} =~ m/^oath/) {
|
||||
$mdoc .= ".In stdint.h\n";
|
||||
}
|
||||
if ($$func{args} =~ m/\bFILE \*\b/) {
|
||||
$mdoc .= ".In stdio.h\n";
|
||||
}
|
||||
if ($$func{name} =~ m/^oath/) {
|
||||
$mdoc .= ".In security/oath.h
|
||||
";
|
||||
}
|
||||
if ($$func{name} =~ m/^(?:open)?pam/) {
|
||||
$mdoc .= ".In security/pam_appl.h
|
||||
";
|
||||
|
@ -541,8 +527,6 @@ on failure.
|
|||
.Fn $$func{name}
|
||||
function is an OpenPAM extension.
|
||||
";
|
||||
} elsif ($$func{oath}) {
|
||||
# nothing yet
|
||||
} else {
|
||||
$mdoc .= ".Rs
|
||||
.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
|
||||
|
@ -573,7 +557,7 @@ sub readproto($) {
|
|||
open(FILE, "<", "$fn")
|
||||
or die("$fn: open(): $!\n");
|
||||
while (<FILE>) {
|
||||
if (m/^\.Nm ((?:(?:open)?pam|oath)_.*?)\s*$/) {
|
||||
if (m/^\.Nm ((?:(?:open)?pam)_.*?)\s*$/) {
|
||||
$func{Nm} = $func{Nm} || $1;
|
||||
} elsif (m/^\.Ft (\S.*?)\s*$/) {
|
||||
$func{Ft} = $func{Ft} || $1;
|
||||
|
@ -621,8 +605,6 @@ sub gensummary($) {
|
|||
.Sh SYNOPSIS\n";
|
||||
if ($page eq 'pam') {
|
||||
print FILE ".In security/pam_appl.h\n";
|
||||
} elsif ($page eq 'oath') {
|
||||
print FILE ".In security/oath.h\n";
|
||||
} else {
|
||||
print FILE ".In security/openpam.h\n";
|
||||
}
|
||||
|
@ -657,21 +639,15 @@ The following return codes are defined by
|
|||
++$xref{3}->{$func};
|
||||
}
|
||||
print FILE genxref(\%xref);
|
||||
if ($page eq 'oath') {
|
||||
print FILE ".Sh AUTHORS
|
||||
The OATH library and this manual page were $AUTHORS{UIO}
|
||||
";
|
||||
} else {
|
||||
print FILE ".Sh STANDARDS
|
||||
print FILE ".Sh STANDARDS
|
||||
.Rs
|
||||
.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
|
||||
.%D \"June 1997\"
|
||||
.Re
|
||||
";
|
||||
print FILE ".Sh AUTHORS
|
||||
print FILE ".Sh AUTHORS
|
||||
The OpenPAM library and this manual page were $AUTHORS{THINKSEC}
|
||||
";
|
||||
}
|
||||
close(FILE);
|
||||
}
|
||||
|
||||
|
@ -685,15 +661,13 @@ MAIN:{
|
|||
my %opts;
|
||||
|
||||
usage()
|
||||
unless (@ARGV && getopts("aop", \%opts));
|
||||
unless (@ARGV && getopts("op", \%opts));
|
||||
$TODAY = strftime("%B %e, %Y", localtime(time()));
|
||||
$TODAY =~ s,\s+, ,g;
|
||||
if ($opts{a} || $opts{o} || $opts{p}) {
|
||||
if ($opts{o} || $opts{p}) {
|
||||
foreach my $fn (@ARGV) {
|
||||
readproto($fn);
|
||||
}
|
||||
gensummary('oath')
|
||||
if ($opts{a});
|
||||
gensummary('openpam')
|
||||
if ($opts{o});
|
||||
gensummary('pam')
|
||||
|
|
|
@ -5,7 +5,3 @@ SUBDIRS = pam_deny pam_permit pam_return
|
|||
if WITH_PAM_UNIX
|
||||
SUBDIRS += pam_unix
|
||||
endif
|
||||
|
||||
if WITH_OATH
|
||||
SUBDIRS += pam_oath
|
||||
endif
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = $(OPENPAM_MODULES_DIR)
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
module_LTLIBRARIES = pam_oath.la
|
||||
|
||||
pam_oath_la_SOURCES = pam_oath.c
|
||||
pam_oath_la_LDFLAGS = -no-undefined -module -version-info $(LIB_MAJ) \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
pam_oath_la_LIBADD =
|
||||
if WITH_SYSTEM_LIBOATH
|
||||
pam_oath_la_LIBADD += $(SYSTEM_LIBOATH)
|
||||
else
|
||||
pam_oath_la_LIBADD += $(top_builddir)/lib/liboath/liboath.la
|
||||
endif
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pam_oath_la_LIBADD += $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pam_oath_la_LIBADD += $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
||||
dist_man8_MANS = pam_oath.8
|
|
@ -1,87 +0,0 @@
|
|||
.\"-
|
||||
.\" Copyright (c) 2012-2016 The University of Oslo
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. The name of the author may not be used to endorse or promote
|
||||
.\" products derived from this software without specific prior written
|
||||
.\" permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
.\"
|
||||
.\"
|
||||
.\" $Id$
|
||||
.\"
|
||||
.Dd January 11, 2016
|
||||
.Dt PAM_OATH 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pam_oath
|
||||
.Nd OATH service module
|
||||
.Sh SYNOPSIS
|
||||
.Op Ar service
|
||||
.Ar module-type
|
||||
.Ar control-flag
|
||||
.Cm Nm
|
||||
.Op Ar arguments
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
service module implements counter-based and time-based one-time
|
||||
passwords.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
service module recognizes the following options:
|
||||
.Bl -tag -width ".Cm echo_pass"
|
||||
.It Cm nokey = Ar fail | fake | ignore
|
||||
Specifies how the module should behave when no key is available for
|
||||
the user: either fail immediately, prompt for a code but fail anyway,
|
||||
or let authentication proceed by other means.
|
||||
\" .It Cm nouser = Ar fail | fake | ignore
|
||||
\" Specifies how the module should behave when the user does not exist.
|
||||
\" See
|
||||
\" .Bm nokey
|
||||
\" above.
|
||||
\" .It Cm badkey = Ar fail | fake | ignore
|
||||
\" Specifies how the module should behave when the user exists and has a
|
||||
\" key, but the key could not be loaded (e.g. due to a syntax error in
|
||||
\" the keyfile).
|
||||
\" See
|
||||
\" .Bm nokey
|
||||
\" above.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
service module uses
|
||||
.Xr pam_get_authtok 3
|
||||
to prompt the user, and will therefore also be affected by the
|
||||
standard options
|
||||
.Sh SEE ALSO
|
||||
.Xr oathkey 1 ,
|
||||
.Xr pam.conf 5 ,
|
||||
.Xr pam 8
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
module and this manual page were developed by
|
||||
.An Dag-Erling Sm\(/orgrav Aq des@des.no
|
||||
for the University of Oslo.
|
|
@ -1,320 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 The University of Oslo
|
||||
* Copyright (c) 2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PAM_SM_AUTH
|
||||
#define PAM_SM_ACCOUNT
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
#define PAM_OATH_PROMPT "Verification code: "
|
||||
#define PAM_OATH_HOTP_WINDOW 3
|
||||
#define PAM_OATH_TOTP_WINDOW 3
|
||||
|
||||
enum pam_oath_nokey { nokey_error = -1, nokey_fail, nokey_fake, nokey_ignore };
|
||||
|
||||
static const char *pam_oath_default_keyfile = "/var/oath/%u.otpauth";
|
||||
|
||||
/*
|
||||
* Parse the nokey or badkey option, which indicates how we should act if
|
||||
* the user has no keyfile or the keyfile is invalid.
|
||||
*/
|
||||
static enum pam_oath_nokey
|
||||
pam_oath_nokey_option(pam_handle_t *pamh, const char *option)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
if ((value = openpam_get_option(pamh, option)) == NULL)
|
||||
return (nokey_fail);
|
||||
else if (strcmp(value, "fail") == 0)
|
||||
return (nokey_fail);
|
||||
else if (strcmp(value, "fake") == 0)
|
||||
return (nokey_fake);
|
||||
else if (strcmp(value, "ignore") == 0)
|
||||
return (nokey_ignore);
|
||||
openpam_log(PAM_LOG_ERROR, "the value of the %s option "
|
||||
"must be either 'fail', 'fake' or 'ignore'", option);
|
||||
return (nokey_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a numeric option. Returns -1 if the option is not set or its
|
||||
* value is not an integer in the range [0, INT_MAX].
|
||||
*/
|
||||
static int
|
||||
pam_oath_int_option(pam_handle_t *pamh, const char *option)
|
||||
{
|
||||
const char *value;
|
||||
char *end;
|
||||
long num;
|
||||
|
||||
if ((value = openpam_get_option(pamh, option)) == NULL)
|
||||
return (-1);
|
||||
num = strtol(value, &end, 10);
|
||||
if (*value == '\0' || *end != '\0' || num < 0 || num > INT_MAX) {
|
||||
openpam_log(PAM_LOG_ERROR, "the value of the %s option "
|
||||
"is invalid.", option);
|
||||
return (-1);
|
||||
}
|
||||
return (num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the location of the user's keyfile.
|
||||
*/
|
||||
static char *
|
||||
pam_oath_keyfile(pam_handle_t *pamh)
|
||||
{
|
||||
const char *keyfile;
|
||||
char *path;
|
||||
size_t size;
|
||||
|
||||
if ((keyfile = openpam_get_option(pamh, "keyfile")) == NULL)
|
||||
keyfile = pam_oath_default_keyfile;
|
||||
size = 0;
|
||||
if (openpam_subst(pamh, NULL, &size, keyfile) != PAM_TRY_AGAIN)
|
||||
return (NULL);
|
||||
if ((path = malloc(size)) == NULL)
|
||||
return (NULL);
|
||||
if (openpam_subst(pamh, path, &size, keyfile) != PAM_SUCCESS) {
|
||||
free(path);
|
||||
return (NULL);
|
||||
}
|
||||
return (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the user's key.
|
||||
*/
|
||||
static struct oath_key *
|
||||
pam_oath_load_key(const char *keyfile)
|
||||
{
|
||||
|
||||
/* XXX should check ownership and permissions */
|
||||
return (oath_key_from_file(keyfile));
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the user's key.
|
||||
* XXX should be a liboath API function.
|
||||
*/
|
||||
static int
|
||||
pam_oath_save_key(const struct oath_key *key, const char *keyfile)
|
||||
{
|
||||
char *keyuri;
|
||||
int fd, len, pam_err;
|
||||
|
||||
keyuri = NULL;
|
||||
len = 0;
|
||||
fd = -1;
|
||||
pam_err = PAM_SYSTEM_ERR;
|
||||
if ((keyuri = oath_key_to_uri(key)) == NULL)
|
||||
goto done;
|
||||
len = strlen(keyuri);
|
||||
if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
|
||||
write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: %m", keyfile);
|
||||
goto done;
|
||||
}
|
||||
pam_err = PAM_SUCCESS;
|
||||
done:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (keyuri != NULL) {
|
||||
memset(keyuri, 0, len);
|
||||
free(keyuri);
|
||||
}
|
||||
return (pam_err);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
enum pam_oath_nokey nokey, badkey;
|
||||
struct passwd *pwd;
|
||||
const char *user, *password;
|
||||
char *end, *keyfile;
|
||||
struct oath_key *key;
|
||||
unsigned long response;
|
||||
int pam_err, ret, window;
|
||||
|
||||
/* unused */
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
keyfile = NULL;
|
||||
key = NULL;
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "attempting OATH authentication");
|
||||
|
||||
/* check how to behave if the user does not have a valid key */
|
||||
if ((nokey = pam_oath_nokey_option(pamh, "nokey")) == nokey_error ||
|
||||
(badkey = pam_oath_nokey_option(pamh, "badkey")) == nokey_error) {
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* identify user */
|
||||
if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
|
||||
goto done;
|
||||
if ((pwd = getpwnam(user)) == NULL) {
|
||||
pam_err = PAM_USER_UNKNOWN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "authenticating user %s", user);
|
||||
|
||||
/* load key */
|
||||
if ((keyfile = pam_oath_keyfile(pamh)) == NULL) {
|
||||
pam_err = PAM_SYSTEM_ERR;
|
||||
goto done;
|
||||
}
|
||||
openpam_log(PAM_LOG_VERBOSE, "attempting to load %s for %s", keyfile, user);
|
||||
key = pam_oath_load_key(keyfile);
|
||||
|
||||
/*
|
||||
* The user doesn't have a key, should we fake it?
|
||||
*
|
||||
* XXX implement badkey - currently, oath_key_from_file() doesn't
|
||||
* provide enough information for us to tell the difference
|
||||
* between a bad key and no key at all.
|
||||
*
|
||||
* XXX move this into pam_oath_load_key()
|
||||
*/
|
||||
if (key == NULL) {
|
||||
openpam_log(PAM_LOG_VERBOSE, "no key found for %s", user);
|
||||
switch (nokey) {
|
||||
case nokey_fail:
|
||||
pam_err = PAM_AUTHINFO_UNAVAIL;
|
||||
goto done;
|
||||
case nokey_fake:
|
||||
key = oath_key_dummy(om_hotp, oh_sha1, 6);
|
||||
break;
|
||||
case nokey_ignore:
|
||||
pam_err = PAM_IGNORE;
|
||||
goto done;
|
||||
default:
|
||||
/* can't happen */
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* get user's response */
|
||||
pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
|
||||
&password, PAM_OATH_PROMPT);
|
||||
if (pam_err != PAM_SUCCESS) {
|
||||
openpam_log(PAM_LOG_VERBOSE, "conversation failure");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* convert to number */
|
||||
response = strtoul(password, &end, 10);
|
||||
if (end == password || *end != '\0')
|
||||
response = ULONG_MAX;
|
||||
|
||||
/* verify response */
|
||||
if (key->mode == om_hotp) {
|
||||
if ((window = pam_oath_int_option(pamh, "hotp_window")) < 0 &&
|
||||
(window = pam_oath_int_option(pamh, "window")) < 0)
|
||||
window = PAM_OATH_HOTP_WINDOW;
|
||||
ret = oath_hotp_match(key, response, window);
|
||||
} else {
|
||||
if ((window = pam_oath_int_option(pamh, "totp_window")) < 0 &&
|
||||
(window = pam_oath_int_option(pamh, "window")) < 0)
|
||||
window = PAM_OATH_TOTP_WINDOW;
|
||||
ret = oath_totp_match(key, response, window);
|
||||
}
|
||||
openpam_log(PAM_LOG_VERBOSE, "verification code %s",
|
||||
ret > 0 ? "matched" : "did not match");
|
||||
if (ret <= 0) {
|
||||
pam_err = ret < 0 ? PAM_SERVICE_ERR : PAM_AUTH_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* write back the key (update counter for HOTP, lastused for TOTP) */
|
||||
if (pam_oath_save_key(key, keyfile) != 0) {
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "OATH authentication succeeded");
|
||||
pam_err = PAM_SUCCESS;
|
||||
done:
|
||||
oath_key_free(key);
|
||||
free(keyfile);
|
||||
return (pam_err);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
/* unused */
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
/* unused */
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_MODULE_ENTRY("pam_unix");
|
|
@ -13,9 +13,6 @@ TESTS += t_openpam_ctype
|
|||
TESTS += t_openpam_dispatch
|
||||
TESTS += t_openpam_readword
|
||||
TESTS += t_openpam_readlinev
|
||||
if WITH_OATH
|
||||
TESTS += t_rfc3986 t_rfc4648
|
||||
endif
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
# libt - common support code
|
||||
|
@ -29,10 +26,3 @@ LDADD += $(SYSTEM_LIBPAM)
|
|||
else
|
||||
LDADD += $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
if WITH_OATH
|
||||
if WITH_SYSTEM_LIBOATH
|
||||
LDADD += $(SYSTEM_LIBOATH)
|
||||
else
|
||||
LDADD += $(top_builddir)/lib/liboath/liboath.la
|
||||
endif
|
||||
endif
|
||||
|
|
160
t/t_rfc3986.c
160
t/t_rfc3986.c
|
@ -1,160 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2015 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#include "t.h"
|
||||
|
||||
struct t_case {
|
||||
const char *desc;
|
||||
size_t (*func)(const char *, size_t, char *, size_t);
|
||||
const char *in; /* input string */
|
||||
size_t ilen; /* input length */
|
||||
const char *out; /* expected output string */
|
||||
size_t olen; /* expected output length */
|
||||
};
|
||||
|
||||
/* basic encoding / decoding */
|
||||
#define T_ENCODE6(d, i, il, o, ol) \
|
||||
{ .func = oath_uri_encode, .desc = d, \
|
||||
.in = i, .ilen = il, .out = o, .olen = ol }
|
||||
#define T_ENCODE5(d, i, il, o, ol) \
|
||||
T_ENCODE6(d, i, il, o, ol)
|
||||
#define T_ENCODE4(d, i, il, o) \
|
||||
T_ENCODE5(d, i, il, o, sizeof o)
|
||||
#define T_ENCODE(d, i, o) \
|
||||
T_ENCODE4(d, i, sizeof i - 1, o)
|
||||
|
||||
#define T_DECODE6(d, i, il, o, ol) \
|
||||
{ .func = oath_uri_decode, .desc = d, \
|
||||
.in = i, .ilen = il, .out = o, .olen = ol }
|
||||
#define T_DECODE5(d, i, il, o, ol) \
|
||||
T_DECODE6(d, i, il, o, ol)
|
||||
#define T_DECODE4(d, i, il, o) \
|
||||
T_DECODE5(d, i, il, o, sizeof o)
|
||||
#define T_DECODE(d, i, o) \
|
||||
T_DECODE4(d, i, sizeof i - 1, o)
|
||||
|
||||
#define T_ENCDEC(d, i, o) \
|
||||
T_ENCODE(d " enc", i, o), T_DECODE(d " dec", o, i)
|
||||
|
||||
static struct t_case t_cases[] = {
|
||||
/* empty */
|
||||
T_DECODE("empty", "", ""),
|
||||
|
||||
/* simple */
|
||||
T_DECODE("simple", "%20", " "),
|
||||
T_DECODE("prefix", "x%20", "x "),
|
||||
T_DECODE("suffix", "%20x", " x"),
|
||||
T_DECODE("surrounded", "x%20x", "x x"),
|
||||
|
||||
/* input overflow */
|
||||
T_DECODE4("short", "%20xy", 4, " x"),
|
||||
|
||||
/* partials */
|
||||
T_DECODE("partial, one", "%", "%"),
|
||||
T_DECODE("partial, two", "%2", "%2"),
|
||||
|
||||
/* non-hex character */
|
||||
T_DECODE("non-hex, first", "%2x", "%2x"),
|
||||
T_DECODE("non-hex, second", "%x0", "%x0"),
|
||||
T_DECODE("non-hex, both", "%xx", "%xx"),
|
||||
};
|
||||
|
||||
/*
|
||||
* Encoding test function
|
||||
*/
|
||||
static int
|
||||
t_rfc3986(void *arg)
|
||||
{
|
||||
struct t_case *t = arg;
|
||||
char buf[256];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
len = t->func(t->in, t->ilen, buf, sizeof buf);
|
||||
ret = 1;
|
||||
if (t->out && len != t->olen) {
|
||||
t_verbose("expected output length %zu, got %zu\n",
|
||||
t->olen, len);
|
||||
ret = 0;
|
||||
}
|
||||
if (t->out && strncmp(buf, t->out, len) != 0) {
|
||||
t_verbose("expected '%.*s' got '%.*s'\n",
|
||||
(int)t->olen, t->out, (int)len, buf);
|
||||
ret = 0;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the test plan
|
||||
*/
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
struct t_test **plan, *tests;
|
||||
int i, n;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
n = sizeof t_cases / sizeof t_cases[0];
|
||||
if ((plan = calloc(n + 1, sizeof *plan)) == NULL ||
|
||||
(tests = calloc(n, sizeof *tests)) == NULL)
|
||||
return (NULL);
|
||||
for (i = 0; i < n; ++i) {
|
||||
plan[i] = &tests[i];
|
||||
tests[i].func = t_rfc3986;
|
||||
tests[i].desc = t_cases[i].desc;
|
||||
tests[i].arg = &t_cases[i];
|
||||
}
|
||||
plan[n] = NULL;
|
||||
return (plan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
void
|
||||
t_cleanup(void)
|
||||
{
|
||||
}
|
250
t/t_rfc4648.c
250
t/t_rfc4648.c
|
@ -1,250 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013-2015 The University of Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#include "t.h"
|
||||
|
||||
struct t_case {
|
||||
const char *desc;
|
||||
int (*func)(const char *, size_t, char *, size_t *);
|
||||
const char *in; /* input string */
|
||||
size_t ilen; /* input length */
|
||||
const char *out; /* expected output string or NULL */
|
||||
size_t blen; /* initial value for olen or 0*/
|
||||
size_t olen; /* expected value for olen */
|
||||
int ret; /* expected return value */
|
||||
int err; /* expected errno if ret != 0 */
|
||||
};
|
||||
|
||||
/* basic encoding / decoding */
|
||||
#define T_ENCODE_N(N, i, o) \
|
||||
{ "base"#N"_enc("#i")", base##N##_enc, i, sizeof i - 1, \
|
||||
o, sizeof o, sizeof o, 0, 0 }
|
||||
#define T_DECODE_N(N, i, o) \
|
||||
{ "base"#N"_dec("#i")", base##N##_dec, i, sizeof i - 1, \
|
||||
o, sizeof o - 1, sizeof o - 1, 0, 0 }
|
||||
#define T_ENCODE(p, b32, b64) \
|
||||
T_ENCODE_N(32, p, b32), T_ENCODE_N(64, p, b64)
|
||||
#define T_DECODE(p, b32, b64) \
|
||||
T_DECODE_N(32, b32, p), T_DECODE_N(64, b64, p)
|
||||
|
||||
/* roundtrip encoding tests */
|
||||
#define T_ENCDEC_N(N, p, e) \
|
||||
T_ENCODE_N(N, p, e), T_DECODE_N(N, e, p)
|
||||
#define T_ENCDEC(p, b32, b64) \
|
||||
T_ENCDEC_N(32, p, b32), T_ENCDEC_N(64, p, b64)
|
||||
|
||||
/* decoding failure */
|
||||
#define T_DECODE_FAIL_N(N, e, i) \
|
||||
{ "base"#N"_dec("#i")", base##N##_dec, i, sizeof i - 1, \
|
||||
NULL, 0, 0, -1, e }
|
||||
#define T_DECODE_FAIL(e, b32, b64) \
|
||||
T_DECODE_FAIL_N(32, e, b32), T_DECODE_FAIL_N(64, e, b64)
|
||||
|
||||
/* input string shorter than input length */
|
||||
#define T_SHORT_INPUT_DEC(N, i) \
|
||||
{ "base"#N"_dec (short input)", base##N##_dec, i, sizeof i + 2, \
|
||||
NULL, 0, base##N##_declen(sizeof i - 1), 0, 0 }
|
||||
#define T_SHORT_INPUT() \
|
||||
T_SHORT_INPUT_DEC(32, "AAAAAAAA"), \
|
||||
T_SHORT_INPUT_DEC(64, "AAAA")
|
||||
|
||||
/* output string longer than output length */
|
||||
#define T_LONG_OUTPUT_ENC(N, i) \
|
||||
{ "base"#N"_enc (long output)", base##N##_enc, i, sizeof i - 1, \
|
||||
NULL, 1, base##N##_enclen(sizeof i - 1) + 1, -1, ENOSPC }
|
||||
#define T_LONG_OUTPUT_DEC(N, i) \
|
||||
{ "base"#N"_dec (long output)", base##N##_dec, "AAAAAAAA", 8, \
|
||||
NULL, 1, base##N##_declen(sizeof i - 1), -1, ENOSPC }
|
||||
#define T_LONG_OUTPUT() \
|
||||
T_LONG_OUTPUT_ENC(32, "foo"), \
|
||||
T_LONG_OUTPUT_DEC(32, "AAAAAAAA"), \
|
||||
T_LONG_OUTPUT_ENC(64, "foo"), \
|
||||
T_LONG_OUTPUT_DEC(64, "AAAA")
|
||||
|
||||
static const char b64alphabet[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
static const char b64complete[] = {
|
||||
0x00, 0x10, 0x83, 0x10, 0x51, 0x87,
|
||||
0x20, 0x92, 0x8b, 0x30, 0xd3, 0x8f,
|
||||
0x41, 0x14, 0x93, 0x51, 0x55, 0x97,
|
||||
0x61, 0x96, 0x9b, 0x71, 0xd7, 0x9f,
|
||||
0x82, 0x18, 0xa3, 0x92, 0x59, 0xa7,
|
||||
0xa2, 0x9a, 0xab, 0xb2, 0xdb, 0xaf,
|
||||
0xc3, 0x1c, 0xb3, 0xd3, 0x5d, 0xb7,
|
||||
0xe3, 0x9e, 0xbb, 0xf3, 0xdf, 0xbf,
|
||||
0x00
|
||||
};
|
||||
|
||||
static const char b32alphabet[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
static const char b32complete[] = {
|
||||
0x00, 0x44, 0x32, 0x14, 0xc7,
|
||||
0x42, 0x54, 0xb6, 0x35, 0xcf,
|
||||
0x84, 0x65, 0x3a, 0x56, 0xd7,
|
||||
0xc6, 0x75, 0xbe, 0x77, 0xdf,
|
||||
0x00
|
||||
};
|
||||
|
||||
static struct t_case t_cases[] = {
|
||||
/* complete alphabet */
|
||||
T_ENCDEC_N(32, b32complete, b32alphabet),
|
||||
T_ENCDEC_N(64, b64complete, b64alphabet),
|
||||
|
||||
/* test vectors from RFC 4648 */
|
||||
/* plain base32 base64 */
|
||||
T_ENCDEC("", "", ""),
|
||||
T_ENCDEC("f", "MY======", "Zg=="),
|
||||
T_ENCDEC("fo", "MZXQ====", "Zm8="),
|
||||
T_ENCDEC("foo", "MZXW6===", "Zm9v"),
|
||||
T_ENCDEC("foob", "MZXW6YQ=", "Zm9vYg=="),
|
||||
T_ENCDEC("fooba", "MZXW6YTB", "Zm9vYmE="),
|
||||
T_ENCDEC("foobar", "MZXW6YTBOI======", "Zm9vYmFy"),
|
||||
|
||||
/* zeroes */
|
||||
T_ENCDEC("\0\0\0", "AAAAA===", "AAAA"),
|
||||
|
||||
/* sloppy padding */
|
||||
T_DECODE("f", "MY=", "Zg="),
|
||||
T_DECODE("f", "MY", "Zg"),
|
||||
|
||||
/* whitespace */
|
||||
/* plain base32 base64 */
|
||||
T_DECODE("tst", "ORZX I===", "dH N0"),
|
||||
T_DECODE("tst", "ORZX\tI===", "dH\tN0"),
|
||||
T_DECODE("tst", "ORZX\rI===", "dH\rN0"),
|
||||
T_DECODE("tst", "ORZX\nI===", "dH\nN0"),
|
||||
|
||||
/* invalid character in data */
|
||||
T_DECODE_FAIL(EINVAL, "AA!AAAAAA", "AA!A"),
|
||||
|
||||
/* invalid character in padding */
|
||||
T_DECODE_FAIL(EINVAL, "AAAAA==!", "AA=!"),
|
||||
|
||||
/* padding with no data */
|
||||
T_DECODE_FAIL(EINVAL, "AAAAAAAA=", "AAAA="),
|
||||
|
||||
/* data after padding */
|
||||
T_DECODE_FAIL(EINVAL, "AA=A", "AA=A"),
|
||||
|
||||
/* padding in incorrect location */
|
||||
T_DECODE_FAIL_N(32, EINVAL, "A======="),
|
||||
T_DECODE_FAIL_N(32, EINVAL, "AAA====="),
|
||||
T_DECODE_FAIL_N(32, EINVAL, "AAAAAA=="),
|
||||
T_DECODE_FAIL_N(64, EINVAL, "A==="),
|
||||
|
||||
/* various error conditions */
|
||||
T_SHORT_INPUT(),
|
||||
T_LONG_OUTPUT(),
|
||||
};
|
||||
|
||||
/*
|
||||
* Encoding test function
|
||||
*/
|
||||
static int
|
||||
t_rfc4648(void *arg)
|
||||
{
|
||||
struct t_case *t = arg;
|
||||
char buf[256];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
len = t->blen ? t->blen : sizeof buf;
|
||||
ret = t->func(t->in, t->ilen, buf, &len);
|
||||
if (ret != t->ret) {
|
||||
t_verbose("expected return code %d, got %d\n",
|
||||
t->ret, ret);
|
||||
return (0);
|
||||
}
|
||||
if (t->out && len != t->olen) {
|
||||
t_verbose("expected output length %zu, got %zu\n",
|
||||
t->olen, len);
|
||||
return (0);
|
||||
}
|
||||
if (t->ret != 0 && errno != t->err) {
|
||||
t_verbose("expected errno %d, got %d\n",
|
||||
t->err, errno);
|
||||
return (0);
|
||||
}
|
||||
if (t->ret == 0 && t->out && strncmp(buf, t->out, len) != 0) {
|
||||
t_verbose("expected '%.*s' got '%.*s'\n",
|
||||
(int)t->olen, t->out, (int)len, buf);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the test plan
|
||||
*/
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
struct t_test **plan, *tests;
|
||||
int i, n;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
n = sizeof t_cases / sizeof t_cases[0];
|
||||
if ((plan = calloc(n + 1, sizeof *plan)) == NULL ||
|
||||
(tests = calloc(n, sizeof *tests)) == NULL)
|
||||
return (NULL);
|
||||
for (i = 0; i < n; ++i) {
|
||||
plan[i] = &tests[i];
|
||||
tests[i].func = t_rfc4648;
|
||||
tests[i].desc = t_cases[i].desc;
|
||||
tests[i].arg = &t_cases[i];
|
||||
}
|
||||
plan[n] = NULL;
|
||||
return (plan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
void
|
||||
t_cleanup(void)
|
||||
{
|
||||
}
|
Loading…
Add table
Reference in a new issue