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