1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-21 11:13:30 +00:00
freebsd/sys/netinet/sctp_auth.c
Randall Stewart 2afb3e849f - During shutdown pending, when the last sack came in and
the last message on the send stream was "null" but still
  there, a state we allow, we could get hung and not clean
  it up and wait for the shutdown guard timer to clear the
  association without a graceful close. Fix this so that
  that we properly clean up.
- Added support for Multiple ASCONF per new RFC. We only
  (so far) accept input of these and cannot yet generate
  a multi-asconf.
- Sysctl'd support for experimental Fast Handover feature. Always
  disabled unless sysctl or socket option changes to enable.
- Error case in add-ip where the peer supports AUTH and ADD-IP
  but does NOT require AUTH of ASCONF/ASCONF-ACK. We need to
  ABORT in this case.
- According to the Kyoto summit of socket api developers
  (Solaris, Linux, BSD). We need to have:
   o non-eeor mode messages be atomic - Fixed
   o Allow implicit setup of an assoc in 1-2-1 model if
     using the sctp_**() send calls - Fixed
   o Get rid of HAVE_XXX declarations - Done
   o add a sctp_pr_policy in hole in sndrcvinfo structure - Done
   o add a PR_SCTP_POLICY_VALID type flag - yet to-do in a future patch!
- Optimize sctp6 calls to reuse code in sctp_usrreq. Also optimize
  when we close sending out the data and disabling Nagle.
- Change key concatenation order to match the auth RFC
- When sending OOTB shutdown_complete always do csum.
- Don't send PKT-DROP to a PKT-DROP
- For abort chunks just always checksums same for
  shutdown-complete.
- inpcb_free front state had a bug where in queue
  data could wedge an assoc. We need to just abandon
  ones in front states (free_assoc).
- If a peer sends us a 64k abort, we would try to
  assemble a response packet which may be larger than
  64k. This then would be dropped by IP. Instead make
  a "minimum" size for us 64k-2k (we want at least
  2k for our initack). If we receive such an init
  discard it early without all the processing.
- When we peel off we must increment the tcb ref count
  to keep it from being freed from underneath us.
- handling fwd-tsn had bugs that caused memory overwrites
  when given faulty data, fixed so can't happen and we
  also stop at the first bad stream no.
- Fixed so comm-up generates the adaption indication.
- peeloff did not get the hmac params copied.
- fix it so we lock the addr list when doing src-addr selection
  (in future we need to use a multi-reader/one writer lock here)
- During lowlevel output, we could end up with a _l_addr set
  to null if the iterator is calling the output routine. This
  means we would possibly crash when we gather the MTU info.
  Fix so we only do the gather where we have a src address
  cached.
- we need to be sure to set abort flag on conn state when
  we receive an abort.
- peeloff could leak a socket. Moved code so the close will
  find the socket if the peeloff fails (uipc_syscalls.c)

Approved by:	re@freebsd.org(Ken Smith)
2007-08-27 05:19:48 +00:00

2470 lines
64 KiB
C

/*-
* Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* a) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* b) 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.
*
* c) Neither the name of Cisco Systems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <netinet/sctp_os.h>
#include <netinet/sctp.h>
#include <netinet/sctp_header.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctp_var.h>
#include <netinet/sctp_sysctl.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_indata.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_auth.h>
#ifdef SCTP_DEBUG
#define SCTP_AUTH_DEBUG (sctp_debug_on & SCTP_DEBUG_AUTH1)
#define SCTP_AUTH_DEBUG2 (sctp_debug_on & SCTP_DEBUG_AUTH2)
#endif /* SCTP_DEBUG */
void
sctp_clear_chunklist(sctp_auth_chklist_t * chklist)
{
bzero(chklist, sizeof(*chklist));
/* chklist->num_chunks = 0; */
}
sctp_auth_chklist_t *
sctp_alloc_chunklist(void)
{
sctp_auth_chklist_t *chklist;
SCTP_MALLOC(chklist, sctp_auth_chklist_t *, sizeof(*chklist),
SCTP_M_AUTH_CL);
if (chklist == NULL) {
SCTPDBG(SCTP_DEBUG_AUTH1, "sctp_alloc_chunklist: failed to get memory!\n");
} else {
sctp_clear_chunklist(chklist);
}
return (chklist);
}
void
sctp_free_chunklist(sctp_auth_chklist_t * list)
{
if (list != NULL)
SCTP_FREE(list, SCTP_M_AUTH_CL);
}
sctp_auth_chklist_t *
sctp_copy_chunklist(sctp_auth_chklist_t * list)
{
sctp_auth_chklist_t *new_list;
if (list == NULL)
return (NULL);
/* get a new list */
new_list = sctp_alloc_chunklist();
if (new_list == NULL)
return (NULL);
/* copy it */
bcopy(list, new_list, sizeof(*new_list));
return (new_list);
}
/*
* add a chunk to the required chunks list
*/
int
sctp_auth_add_chunk(uint8_t chunk, sctp_auth_chklist_t * list)
{
if (list == NULL)
return (-1);
/* is chunk restricted? */
if ((chunk == SCTP_INITIATION) ||
(chunk == SCTP_INITIATION_ACK) ||
(chunk == SCTP_SHUTDOWN_COMPLETE) ||
(chunk == SCTP_AUTHENTICATION)) {
return (-1);
}
if (list->chunks[chunk] == 0) {
list->chunks[chunk] = 1;
list->num_chunks++;
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: added chunk %u (0x%02x) to Auth list\n",
chunk, chunk);
}
return (0);
}
/*
* delete a chunk from the required chunks list
*/
int
sctp_auth_delete_chunk(uint8_t chunk, sctp_auth_chklist_t * list)
{
if (list == NULL)
return (-1);
/* is chunk restricted? */
if ((chunk == SCTP_ASCONF) ||
(chunk == SCTP_ASCONF_ACK)) {
return (-1);
}
if (list->chunks[chunk] == 1) {
list->chunks[chunk] = 0;
list->num_chunks--;
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: deleted chunk %u (0x%02x) from Auth list\n",
chunk, chunk);
}
return (0);
}
size_t
sctp_auth_get_chklist_size(const sctp_auth_chklist_t * list)
{
if (list == NULL)
return (0);
else
return (list->num_chunks);
}
/*
* set the default list of chunks requiring AUTH
*/
void
sctp_auth_set_default_chunks(sctp_auth_chklist_t * list)
{
(void)sctp_auth_add_chunk(SCTP_ASCONF, list);
(void)sctp_auth_add_chunk(SCTP_ASCONF_ACK, list);
}
/*
* return the current number and list of required chunks caller must
* guarantee ptr has space for up to 256 bytes
*/
int
sctp_serialize_auth_chunks(const sctp_auth_chklist_t * list, uint8_t * ptr)
{
int i, count = 0;
if (list == NULL)
return (0);
for (i = 0; i < 256; i++) {
if (list->chunks[i] != 0) {
*ptr++ = i;
count++;
}
}
return (count);
}
int
sctp_pack_auth_chunks(const sctp_auth_chklist_t * list, uint8_t * ptr)
{
int i, size = 0;
if (list == NULL)
return (0);
if (list->num_chunks <= 32) {
/* just list them, one byte each */
for (i = 0; i < 256; i++) {
if (list->chunks[i] != 0) {
*ptr++ = i;
size++;
}
}
} else {
int index, offset;
/* pack into a 32 byte bitfield */
for (i = 0; i < 256; i++) {
if (list->chunks[i] != 0) {
index = i / 8;
offset = i % 8;
ptr[index] |= (1 << offset);
}
}
size = 32;
}
return (size);
}
int
sctp_unpack_auth_chunks(const uint8_t * ptr, uint8_t num_chunks,
sctp_auth_chklist_t * list)
{
int i;
int size;
if (list == NULL)
return (0);
if (num_chunks <= 32) {
/* just pull them, one byte each */
for (i = 0; i < num_chunks; i++) {
(void)sctp_auth_add_chunk(*ptr++, list);
}
size = num_chunks;
} else {
int index, offset;
/* unpack from a 32 byte bitfield */
for (index = 0; index < 32; index++) {
for (offset = 0; offset < 8; offset++) {
if (ptr[index] & (1 << offset)) {
(void)sctp_auth_add_chunk((index * 8) + offset, list);
}
}
}
size = 32;
}
return (size);
}
/*
* allocate structure space for a key of length keylen
*/
sctp_key_t *
sctp_alloc_key(uint32_t keylen)
{
sctp_key_t *new_key;
SCTP_MALLOC(new_key, sctp_key_t *, sizeof(*new_key) + keylen,
SCTP_M_AUTH_KY);
if (new_key == NULL) {
/* out of memory */
return (NULL);
}
new_key->keylen = keylen;
return (new_key);
}
void
sctp_free_key(sctp_key_t * key)
{
if (key != NULL)
SCTP_FREE(key, SCTP_M_AUTH_KY);
}
void
sctp_print_key(sctp_key_t * key, const char *str)
{
uint32_t i;
if (key == NULL) {
printf("%s: [Null key]\n", str);
return;
}
printf("%s: len %u, ", str, key->keylen);
if (key->keylen) {
for (i = 0; i < key->keylen; i++)
printf("%02x", key->key[i]);
printf("\n");
} else {
printf("[Null key]\n");
}
}
void
sctp_show_key(sctp_key_t * key, const char *str)
{
uint32_t i;
if (key == NULL) {
printf("%s: [Null key]\n", str);
return;
}
printf("%s: len %u, ", str, key->keylen);
if (key->keylen) {
for (i = 0; i < key->keylen; i++)
printf("%02x", key->key[i]);
printf("\n");
} else {
printf("[Null key]\n");
}
}
static uint32_t
sctp_get_keylen(sctp_key_t * key)
{
if (key != NULL)
return (key->keylen);
else
return (0);
}
/*
* generate a new random key of length 'keylen'
*/
sctp_key_t *
sctp_generate_random_key(uint32_t keylen)
{
sctp_key_t *new_key;
/* validate keylen */
if (keylen > SCTP_AUTH_RANDOM_SIZE_MAX)
keylen = SCTP_AUTH_RANDOM_SIZE_MAX;
new_key = sctp_alloc_key(keylen);
if (new_key == NULL) {
/* out of memory */
return (NULL);
}
SCTP_READ_RANDOM(new_key->key, keylen);
new_key->keylen = keylen;
return (new_key);
}
sctp_key_t *
sctp_set_key(uint8_t * key, uint32_t keylen)
{
sctp_key_t *new_key;
new_key = sctp_alloc_key(keylen);
if (new_key == NULL) {
/* out of memory */
return (NULL);
}
bcopy(key, new_key->key, keylen);
return (new_key);
}
/*
* given two keys of variable size, compute which key is "larger/smaller"
* returns: 1 if key1 > key2 -1 if key1 < key2 0 if key1 = key2
*/
static int
sctp_compare_key(sctp_key_t * key1, sctp_key_t * key2)
{
uint32_t maxlen;
uint32_t i;
uint32_t key1len, key2len;
uint8_t *key_1, *key_2;
uint8_t temp[SCTP_AUTH_RANDOM_SIZE_MAX];
/* sanity/length check */
key1len = sctp_get_keylen(key1);
key2len = sctp_get_keylen(key2);
if ((key1len == 0) && (key2len == 0))
return (0);
else if (key1len == 0)
return (-1);
else if (key2len == 0)
return (1);
if (key1len != key2len) {
if (key1len >= key2len)
maxlen = key1len;
else
maxlen = key2len;
bzero(temp, maxlen);
if (key1len < maxlen) {
/* prepend zeroes to key1 */
bcopy(key1->key, temp + (maxlen - key1len), key1len);
key_1 = temp;
key_2 = key2->key;
} else {
/* prepend zeroes to key2 */
bcopy(key2->key, temp + (maxlen - key2len), key2len);
key_1 = key1->key;
key_2 = temp;
}
} else {
maxlen = key1len;
key_1 = key1->key;
key_2 = key2->key;
}
for (i = 0; i < maxlen; i++) {
if (*key_1 > *key_2)
return (1);
else if (*key_1 < *key_2)
return (-1);
key_1++;
key_2++;
}
/* keys are equal value, so check lengths */
if (key1len == key2len)
return (0);
else if (key1len < key2len)
return (-1);
else
return (1);
}
/*
* generate the concatenated keying material based on the two keys and the
* shared key (if available). draft-ietf-tsvwg-auth specifies the specific
* order for concatenation
*/
sctp_key_t *
sctp_compute_hashkey(sctp_key_t * key1, sctp_key_t * key2, sctp_key_t * shared)
{
uint32_t keylen;
sctp_key_t *new_key;
uint8_t *key_ptr;
keylen = sctp_get_keylen(key1) + sctp_get_keylen(key2) +
sctp_get_keylen(shared);
if (keylen > 0) {
/* get space for the new key */
new_key = sctp_alloc_key(keylen);
if (new_key == NULL) {
/* out of memory */
return (NULL);
}
new_key->keylen = keylen;
key_ptr = new_key->key;
} else {
/* all keys empty/null?! */
return (NULL);
}
/* concatenate the keys */
if (sctp_compare_key(key1, key2) <= 0) {
#ifdef SCTP_AUTH_DRAFT_04
/* key is key1 + shared + key2 */
if (sctp_get_keylen(key1)) {
bcopy(key1->key, key_ptr, key1->keylen);
key_ptr += key1->keylen;
}
if (sctp_get_keylen(shared)) {
bcopy(shared->key, key_ptr, shared->keylen);
key_ptr += shared->keylen;
}
if (sctp_get_keylen(key2)) {
bcopy(key2->key, key_ptr, key2->keylen);
key_ptr += key2->keylen;
}
#else
/* key is shared + key1 + key2 */
if (sctp_get_keylen(shared)) {
bcopy(shared->key, key_ptr, shared->keylen);
key_ptr += shared->keylen;
}
if (sctp_get_keylen(key1)) {
bcopy(key1->key, key_ptr, key1->keylen);
key_ptr += key1->keylen;
}
if (sctp_get_keylen(key2)) {
bcopy(key2->key, key_ptr, key2->keylen);
key_ptr += key2->keylen;
}
#endif
} else {
#ifdef SCTP_AUTH_DRAFT_04
/* key is key2 + shared + key1 */
if (sctp_get_keylen(key2)) {
bcopy(key2->key, key_ptr, key2->keylen);
key_ptr += key2->keylen;
}
if (sctp_get_keylen(shared)) {
bcopy(shared->key, key_ptr, shared->keylen);
key_ptr += shared->keylen;
}
if (sctp_get_keylen(key1)) {
bcopy(key1->key, key_ptr, key1->keylen);
key_ptr += key1->keylen;
}
#else
/* key is shared + key2 + key1 */
if (sctp_get_keylen(shared)) {
bcopy(shared->key, key_ptr, shared->keylen);
key_ptr += shared->keylen;
}
if (sctp_get_keylen(key2)) {
bcopy(key2->key, key_ptr, key2->keylen);
key_ptr += key2->keylen;
}
if (sctp_get_keylen(key1)) {
bcopy(key1->key, key_ptr, key1->keylen);
key_ptr += key1->keylen;
}
#endif
}
return (new_key);
}
sctp_sharedkey_t *
sctp_alloc_sharedkey(void)
{
sctp_sharedkey_t *new_key;
SCTP_MALLOC(new_key, sctp_sharedkey_t *, sizeof(*new_key),
SCTP_M_AUTH_KY);
if (new_key == NULL) {
/* out of memory */
return (NULL);
}
new_key->keyid = 0;
new_key->key = NULL;
return (new_key);
}
void
sctp_free_sharedkey(sctp_sharedkey_t * skey)
{
if (skey != NULL) {
if (skey->key != NULL)
sctp_free_key(skey->key);
SCTP_FREE(skey, SCTP_M_AUTH_KY);
}
}
sctp_sharedkey_t *
sctp_find_sharedkey(struct sctp_keyhead *shared_keys, uint16_t key_id)
{
sctp_sharedkey_t *skey;
LIST_FOREACH(skey, shared_keys, next) {
if (skey->keyid == key_id)
return (skey);
}
return (NULL);
}
void
sctp_insert_sharedkey(struct sctp_keyhead *shared_keys,
sctp_sharedkey_t * new_skey)
{
sctp_sharedkey_t *skey;
if ((shared_keys == NULL) || (new_skey == NULL))
return;
/* insert into an empty list? */
if (SCTP_LIST_EMPTY(shared_keys)) {
LIST_INSERT_HEAD(shared_keys, new_skey, next);
return;
}
/* insert into the existing list, ordered by key id */
LIST_FOREACH(skey, shared_keys, next) {
if (new_skey->keyid < skey->keyid) {
/* insert it before here */
LIST_INSERT_BEFORE(skey, new_skey, next);
return;
} else if (new_skey->keyid == skey->keyid) {
/* replace the existing key */
SCTPDBG(SCTP_DEBUG_AUTH1,
"replacing shared key id %u\n",
new_skey->keyid);
LIST_INSERT_BEFORE(skey, new_skey, next);
LIST_REMOVE(skey, next);
sctp_free_sharedkey(skey);
return;
}
if (LIST_NEXT(skey, next) == NULL) {
/* belongs at the end of the list */
LIST_INSERT_AFTER(skey, new_skey, next);
return;
}
}
}
static sctp_sharedkey_t *
sctp_copy_sharedkey(const sctp_sharedkey_t * skey)
{
sctp_sharedkey_t *new_skey;
if (skey == NULL)
return (NULL);
new_skey = sctp_alloc_sharedkey();
if (new_skey == NULL)
return (NULL);
if (skey->key != NULL)
new_skey->key = sctp_set_key(skey->key->key, skey->key->keylen);
else
new_skey->key = NULL;
new_skey->keyid = skey->keyid;
return (new_skey);
}
int
sctp_copy_skeylist(const struct sctp_keyhead *src, struct sctp_keyhead *dest)
{
sctp_sharedkey_t *skey, *new_skey;
int count = 0;
if ((src == NULL) || (dest == NULL))
return (0);
LIST_FOREACH(skey, src, next) {
new_skey = sctp_copy_sharedkey(skey);
if (new_skey != NULL) {
sctp_insert_sharedkey(dest, new_skey);
count++;
}
}
return (count);
}
sctp_hmaclist_t *
sctp_alloc_hmaclist(uint8_t num_hmacs)
{
sctp_hmaclist_t *new_list;
int alloc_size;
alloc_size = sizeof(*new_list) + num_hmacs * sizeof(new_list->hmac[0]);
SCTP_MALLOC(new_list, sctp_hmaclist_t *, alloc_size,
SCTP_M_AUTH_HL);
if (new_list == NULL) {
/* out of memory */
return (NULL);
}
new_list->max_algo = num_hmacs;
new_list->num_algo = 0;
return (new_list);
}
void
sctp_free_hmaclist(sctp_hmaclist_t * list)
{
if (list != NULL) {
SCTP_FREE(list, SCTP_M_AUTH_HL);
list = NULL;
}
}
int
sctp_auth_add_hmacid(sctp_hmaclist_t * list, uint16_t hmac_id)
{
int i;
if (list == NULL)
return (-1);
if (list->num_algo == list->max_algo) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: HMAC id list full, ignoring add %u\n", hmac_id);
return (-1);
}
if ((hmac_id != SCTP_AUTH_HMAC_ID_SHA1) &&
#ifdef HAVE_SHA224
(hmac_id != SCTP_AUTH_HMAC_ID_SHA224) &&
#endif
#ifdef HAVE_SHA2
(hmac_id != SCTP_AUTH_HMAC_ID_SHA256) &&
(hmac_id != SCTP_AUTH_HMAC_ID_SHA384) &&
(hmac_id != SCTP_AUTH_HMAC_ID_SHA512) &&
#endif
(hmac_id != SCTP_AUTH_HMAC_ID_MD5)) {
return (-1);
}
/* Now is it already in the list */
for (i = 0; i < list->num_algo; i++) {
if (list->hmac[i] == hmac_id) {
/* already in list */
return (-1);
}
}
SCTPDBG(SCTP_DEBUG_AUTH1, "SCTP: add HMAC id %u to list\n", hmac_id);
list->hmac[list->num_algo++] = hmac_id;
return (0);
}
sctp_hmaclist_t *
sctp_copy_hmaclist(sctp_hmaclist_t * list)
{
sctp_hmaclist_t *new_list;
int i;
if (list == NULL)
return (NULL);
/* get a new list */
new_list = sctp_alloc_hmaclist(list->max_algo);
if (new_list == NULL)
return (NULL);
/* copy it */
new_list->max_algo = list->max_algo;
new_list->num_algo = list->num_algo;
for (i = 0; i < list->num_algo; i++)
new_list->hmac[i] = list->hmac[i];
return (new_list);
}
sctp_hmaclist_t *
sctp_default_supported_hmaclist(void)
{
sctp_hmaclist_t *new_list;
new_list = sctp_alloc_hmaclist(2);
if (new_list == NULL)
return (NULL);
(void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA1);
(void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA256);
return (new_list);
}
/*
* HMAC algos are listed in priority/preference order find the best HMAC id
* to use for the peer based on local support
*/
uint16_t
sctp_negotiate_hmacid(sctp_hmaclist_t * peer, sctp_hmaclist_t * local)
{
int i, j;
if ((local == NULL) || (peer == NULL))
return (SCTP_AUTH_HMAC_ID_RSVD);
for (i = 0; i < peer->num_algo; i++) {
for (j = 0; j < local->num_algo; j++) {
if (peer->hmac[i] == local->hmac[j]) {
#ifndef SCTP_AUTH_DRAFT_04
/* "skip" MD5 as it's been deprecated */
if (peer->hmac[i] == SCTP_AUTH_HMAC_ID_MD5)
continue;
#endif
/* found the "best" one */
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: negotiated peer HMAC id %u\n",
peer->hmac[i]);
return (peer->hmac[i]);
}
}
}
/* didn't find one! */
return (SCTP_AUTH_HMAC_ID_RSVD);
}
/*
* serialize the HMAC algo list and return space used caller must guarantee
* ptr has appropriate space
*/
int
sctp_serialize_hmaclist(sctp_hmaclist_t * list, uint8_t * ptr)
{
int i;
uint16_t hmac_id;
if (list == NULL)
return (0);
for (i = 0; i < list->num_algo; i++) {
hmac_id = htons(list->hmac[i]);
bcopy(&hmac_id, ptr, sizeof(hmac_id));
ptr += sizeof(hmac_id);
}
return (list->num_algo * sizeof(hmac_id));
}
int
sctp_verify_hmac_param(struct sctp_auth_hmac_algo *hmacs, uint32_t num_hmacs)
{
uint32_t i;
uint16_t hmac_id;
uint32_t sha1_supported = 0;
for (i = 0; i < num_hmacs; i++) {
hmac_id = ntohs(hmacs->hmac_ids[i]);
if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1)
sha1_supported = 1;
}
/* all HMAC id's are supported */
if (sha1_supported == 0)
return (-1);
else
return (0);
}
sctp_authinfo_t *
sctp_alloc_authinfo(void)
{
sctp_authinfo_t *new_authinfo;
SCTP_MALLOC(new_authinfo, sctp_authinfo_t *, sizeof(*new_authinfo),
SCTP_M_AUTH_IF);
if (new_authinfo == NULL) {
/* out of memory */
return (NULL);
}
bzero(new_authinfo, sizeof(*new_authinfo));
return (new_authinfo);
}
void
sctp_free_authinfo(sctp_authinfo_t * authinfo)
{
if (authinfo == NULL)
return;
if (authinfo->random != NULL)
sctp_free_key(authinfo->random);
if (authinfo->peer_random != NULL)
sctp_free_key(authinfo->peer_random);
if (authinfo->assoc_key != NULL)
sctp_free_key(authinfo->assoc_key);
if (authinfo->recv_key != NULL)
sctp_free_key(authinfo->recv_key);
/* We are NOT dynamically allocating authinfo's right now... */
/* SCTP_FREE(authinfo, SCTP_M_AUTH_??); */
}
uint32_t
sctp_get_auth_chunk_len(uint16_t hmac_algo)
{
int size;
size = sizeof(struct sctp_auth_chunk) + sctp_get_hmac_digest_len(hmac_algo);
return (SCTP_SIZE32(size));
}
uint32_t
sctp_get_hmac_digest_len(uint16_t hmac_algo)
{
switch (hmac_algo) {
case SCTP_AUTH_HMAC_ID_SHA1:
return (SCTP_AUTH_DIGEST_LEN_SHA1);
case SCTP_AUTH_HMAC_ID_MD5:
return (SCTP_AUTH_DIGEST_LEN_MD5);
#ifdef HAVE_SHA224
case SCTP_AUTH_HMAC_ID_SHA224:
return (SCTP_AUTH_DIGEST_LEN_SHA224);
#endif
#ifdef HAVE_SHA2
case SCTP_AUTH_HMAC_ID_SHA256:
return (SCTP_AUTH_DIGEST_LEN_SHA256);
case SCTP_AUTH_HMAC_ID_SHA384:
return (SCTP_AUTH_DIGEST_LEN_SHA384);
case SCTP_AUTH_HMAC_ID_SHA512:
return (SCTP_AUTH_DIGEST_LEN_SHA512);
#endif
default:
/* unknown HMAC algorithm: can't do anything */
return (0);
} /* end switch */
}
static inline int
sctp_get_hmac_block_len(uint16_t hmac_algo)
{
switch (hmac_algo) {
case SCTP_AUTH_HMAC_ID_SHA1:
case SCTP_AUTH_HMAC_ID_MD5:
#ifdef HAVE_SHA224
case SCTP_AUTH_HMAC_ID_SHA224:
#endif
return (64);
#ifdef HAVE_SHA2
case SCTP_AUTH_HMAC_ID_SHA256:
return (64);
case SCTP_AUTH_HMAC_ID_SHA384:
case SCTP_AUTH_HMAC_ID_SHA512:
return (128);
#endif
case SCTP_AUTH_HMAC_ID_RSVD:
default:
/* unknown HMAC algorithm: can't do anything */
return (0);
} /* end switch */
}
static void
sctp_hmac_init(uint16_t hmac_algo, sctp_hash_context_t * ctx)
{
switch (hmac_algo) {
case SCTP_AUTH_HMAC_ID_SHA1:
SHA1_Init(&ctx->sha1);
break;
case SCTP_AUTH_HMAC_ID_MD5:
MD5_Init(&ctx->md5);
break;
#ifdef HAVE_SHA224
case SCTP_AUTH_HMAC_ID_SHA224:
break;
#endif
#ifdef HAVE_SHA2
case SCTP_AUTH_HMAC_ID_SHA256:
SHA256_Init(&ctx->sha256);
break;
case SCTP_AUTH_HMAC_ID_SHA384:
SHA384_Init(&ctx->sha384);
break;
case SCTP_AUTH_HMAC_ID_SHA512:
SHA512_Init(&ctx->sha512);
break;
#endif
case SCTP_AUTH_HMAC_ID_RSVD:
default:
/* unknown HMAC algorithm: can't do anything */
return;
} /* end switch */
}
static void
sctp_hmac_update(uint16_t hmac_algo, sctp_hash_context_t * ctx,
uint8_t * text, uint32_t textlen)
{
switch (hmac_algo) {
case SCTP_AUTH_HMAC_ID_SHA1:
SHA1_Update(&ctx->sha1, text, textlen);
break;
case SCTP_AUTH_HMAC_ID_MD5:
MD5_Update(&ctx->md5, text, textlen);
break;
#ifdef HAVE_SHA224
case SCTP_AUTH_HMAC_ID_SHA224:
break;
#endif
#ifdef HAVE_SHA2
case SCTP_AUTH_HMAC_ID_SHA256:
SHA256_Update(&ctx->sha256, text, textlen);
break;
case SCTP_AUTH_HMAC_ID_SHA384:
SHA384_Update(&ctx->sha384, text, textlen);
break;
case SCTP_AUTH_HMAC_ID_SHA512:
SHA512_Update(&ctx->sha512, text, textlen);
break;
#endif
case SCTP_AUTH_HMAC_ID_RSVD:
default:
/* unknown HMAC algorithm: can't do anything */
return;
} /* end switch */
}
static void
sctp_hmac_final(uint16_t hmac_algo, sctp_hash_context_t * ctx,
uint8_t * digest)
{
switch (hmac_algo) {
case SCTP_AUTH_HMAC_ID_SHA1:
SHA1_Final(digest, &ctx->sha1);
break;
case SCTP_AUTH_HMAC_ID_MD5:
MD5_Final(digest, &ctx->md5);
break;
#ifdef HAVE_SHA224
case SCTP_AUTH_HMAC_ID_SHA224:
break;
#endif
#ifdef HAVE_SHA2
case SCTP_AUTH_HMAC_ID_SHA256:
SHA256_Final(digest, &ctx->sha256);
break;
case SCTP_AUTH_HMAC_ID_SHA384:
/* SHA384 is truncated SHA512 */
SHA384_Final(digest, &ctx->sha384);
break;
case SCTP_AUTH_HMAC_ID_SHA512:
SHA512_Final(digest, &ctx->sha512);
break;
#endif
case SCTP_AUTH_HMAC_ID_RSVD:
default:
/* unknown HMAC algorithm: can't do anything */
return;
} /* end switch */
}
/*
* Keyed-Hashing for Message Authentication: FIPS 198 (RFC 2104)
*
* Compute the HMAC digest using the desired hash key, text, and HMAC
* algorithm. Resulting digest is placed in 'digest' and digest length
* is returned, if the HMAC was performed.
*
* WARNING: it is up to the caller to supply sufficient space to hold the
* resultant digest.
*/
uint32_t
sctp_hmac(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
uint8_t * text, uint32_t textlen, uint8_t * digest)
{
uint32_t digestlen;
uint32_t blocklen;
sctp_hash_context_t ctx;
uint8_t ipad[128], opad[128]; /* keyed hash inner/outer pads */
uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
uint32_t i;
/* sanity check the material and length */
if ((key == NULL) || (keylen == 0) || (text == NULL) ||
(textlen == 0) || (digest == NULL)) {
/* can't do HMAC with empty key or text or digest store */
return (0);
}
/* validate the hmac algo and get the digest length */
digestlen = sctp_get_hmac_digest_len(hmac_algo);
if (digestlen == 0)
return (0);
/* hash the key if it is longer than the hash block size */
blocklen = sctp_get_hmac_block_len(hmac_algo);
if (keylen > blocklen) {
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, key, keylen);
sctp_hmac_final(hmac_algo, &ctx, temp);
/* set the hashed key as the key */
keylen = digestlen;
key = temp;
}
/* initialize the inner/outer pads with the key and "append" zeroes */
bzero(ipad, blocklen);
bzero(opad, blocklen);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
/* XOR the key with ipad and opad values */
for (i = 0; i < blocklen; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
/* perform inner hash */
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, ipad, blocklen);
sctp_hmac_update(hmac_algo, &ctx, text, textlen);
sctp_hmac_final(hmac_algo, &ctx, temp);
/* perform outer hash */
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, opad, blocklen);
sctp_hmac_update(hmac_algo, &ctx, temp, digestlen);
sctp_hmac_final(hmac_algo, &ctx, digest);
return (digestlen);
}
/* mbuf version */
uint32_t
sctp_hmac_m(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
struct mbuf *m, uint32_t m_offset, uint8_t * digest, uint32_t trailer)
{
uint32_t digestlen;
uint32_t blocklen;
sctp_hash_context_t ctx;
uint8_t ipad[128], opad[128]; /* keyed hash inner/outer pads */
uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
uint32_t i;
struct mbuf *m_tmp;
/* sanity check the material and length */
if ((key == NULL) || (keylen == 0) || (m == NULL) || (digest == NULL)) {
/* can't do HMAC with empty key or text or digest store */
return (0);
}
/* validate the hmac algo and get the digest length */
digestlen = sctp_get_hmac_digest_len(hmac_algo);
if (digestlen == 0)
return (0);
/* hash the key if it is longer than the hash block size */
blocklen = sctp_get_hmac_block_len(hmac_algo);
if (keylen > blocklen) {
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, key, keylen);
sctp_hmac_final(hmac_algo, &ctx, temp);
/* set the hashed key as the key */
keylen = digestlen;
key = temp;
}
/* initialize the inner/outer pads with the key and "append" zeroes */
bzero(ipad, blocklen);
bzero(opad, blocklen);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
/* XOR the key with ipad and opad values */
for (i = 0; i < blocklen; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
/* perform inner hash */
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, ipad, blocklen);
/* find the correct starting mbuf and offset (get start of text) */
m_tmp = m;
while ((m_tmp != NULL) && (m_offset >= (uint32_t) SCTP_BUF_LEN(m_tmp))) {
m_offset -= SCTP_BUF_LEN(m_tmp);
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
/* now use the rest of the mbuf chain for the text */
while (m_tmp != NULL) {
if ((SCTP_BUF_NEXT(m_tmp) == NULL) && trailer) {
sctp_hmac_update(hmac_algo, &ctx, mtod(m_tmp, uint8_t *) + m_offset,
SCTP_BUF_LEN(m_tmp) - (trailer + m_offset));
} else {
sctp_hmac_update(hmac_algo, &ctx, mtod(m_tmp, uint8_t *) + m_offset,
SCTP_BUF_LEN(m_tmp) - m_offset);
}
/* clear the offset since it's only for the first mbuf */
m_offset = 0;
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
sctp_hmac_final(hmac_algo, &ctx, temp);
/* perform outer hash */
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, opad, blocklen);
sctp_hmac_update(hmac_algo, &ctx, temp, digestlen);
sctp_hmac_final(hmac_algo, &ctx, digest);
return (digestlen);
}
/*
* verify the HMAC digest using the desired hash key, text, and HMAC
* algorithm. Returns -1 on error, 0 on success.
*/
int
sctp_verify_hmac(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
uint8_t * text, uint32_t textlen,
uint8_t * digest, uint32_t digestlen)
{
uint32_t len;
uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
/* sanity check the material and length */
if ((key == NULL) || (keylen == 0) ||
(text == NULL) || (textlen == 0) || (digest == NULL)) {
/* can't do HMAC with empty key or text or digest */
return (-1);
}
len = sctp_get_hmac_digest_len(hmac_algo);
if ((len == 0) || (digestlen != len))
return (-1);
/* compute the expected hash */
if (sctp_hmac(hmac_algo, key, keylen, text, textlen, temp) != len)
return (-1);
if (memcmp(digest, temp, digestlen) != 0)
return (-1);
else
return (0);
}
/*
* computes the requested HMAC using a key struct (which may be modified if
* the keylen exceeds the HMAC block len).
*/
uint32_t
sctp_compute_hmac(uint16_t hmac_algo, sctp_key_t * key, uint8_t * text,
uint32_t textlen, uint8_t * digest)
{
uint32_t digestlen;
uint32_t blocklen;
sctp_hash_context_t ctx;
uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
/* sanity check */
if ((key == NULL) || (text == NULL) || (textlen == 0) ||
(digest == NULL)) {
/* can't do HMAC with empty key or text or digest store */
return (0);
}
/* validate the hmac algo and get the digest length */
digestlen = sctp_get_hmac_digest_len(hmac_algo);
if (digestlen == 0)
return (0);
/* hash the key if it is longer than the hash block size */
blocklen = sctp_get_hmac_block_len(hmac_algo);
if (key->keylen > blocklen) {
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, key->key, key->keylen);
sctp_hmac_final(hmac_algo, &ctx, temp);
/* save the hashed key as the new key */
key->keylen = digestlen;
bcopy(temp, key->key, key->keylen);
}
return (sctp_hmac(hmac_algo, key->key, key->keylen, text, textlen,
digest));
}
/* mbuf version */
uint32_t
sctp_compute_hmac_m(uint16_t hmac_algo, sctp_key_t * key, struct mbuf *m,
uint32_t m_offset, uint8_t * digest)
{
uint32_t digestlen;
uint32_t blocklen;
sctp_hash_context_t ctx;
uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
/* sanity check */
if ((key == NULL) || (m == NULL) || (digest == NULL)) {
/* can't do HMAC with empty key or text or digest store */
return (0);
}
/* validate the hmac algo and get the digest length */
digestlen = sctp_get_hmac_digest_len(hmac_algo);
if (digestlen == 0)
return (0);
/* hash the key if it is longer than the hash block size */
blocklen = sctp_get_hmac_block_len(hmac_algo);
if (key->keylen > blocklen) {
sctp_hmac_init(hmac_algo, &ctx);
sctp_hmac_update(hmac_algo, &ctx, key->key, key->keylen);
sctp_hmac_final(hmac_algo, &ctx, temp);
/* save the hashed key as the new key */
key->keylen = digestlen;
bcopy(temp, key->key, key->keylen);
}
return (sctp_hmac_m(hmac_algo, key->key, key->keylen, m, m_offset, digest, 0));
}
int
sctp_auth_is_supported_hmac(sctp_hmaclist_t * list, uint16_t id)
{
int i;
if ((list == NULL) || (id == SCTP_AUTH_HMAC_ID_RSVD))
return (0);
for (i = 0; i < list->num_algo; i++)
if (list->hmac[i] == id)
return (1);
/* not in the list */
return (0);
}
/*
* clear any cached key(s) if they match the given key id on an association
* the cached key(s) will be recomputed and re-cached at next use. ASSUMES
* TCB_LOCK is already held
*/
void
sctp_clear_cachedkeys(struct sctp_tcb *stcb, uint16_t keyid)
{
if (stcb == NULL)
return;
if (keyid == stcb->asoc.authinfo.assoc_keyid) {
sctp_free_key(stcb->asoc.authinfo.assoc_key);
stcb->asoc.authinfo.assoc_key = NULL;
}
if (keyid == stcb->asoc.authinfo.recv_keyid) {
sctp_free_key(stcb->asoc.authinfo.recv_key);
stcb->asoc.authinfo.recv_key = NULL;
}
}
/*
* clear any cached key(s) if they match the given key id for all assocs on
* an association ASSUMES INP_WLOCK is already held
*/
void
sctp_clear_cachedkeys_ep(struct sctp_inpcb *inp, uint16_t keyid)
{
struct sctp_tcb *stcb;
if (inp == NULL)
return;
/* clear the cached keys on all assocs on this instance */
LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
SCTP_TCB_LOCK(stcb);
sctp_clear_cachedkeys(stcb, keyid);
SCTP_TCB_UNLOCK(stcb);
}
}
/*
* delete a shared key from an association ASSUMES TCB_LOCK is already held
*/
int
sctp_delete_sharedkey(struct sctp_tcb *stcb, uint16_t keyid)
{
sctp_sharedkey_t *skey;
if (stcb == NULL)
return (-1);
/* is the keyid the assoc active sending key */
if (keyid == stcb->asoc.authinfo.assoc_keyid)
return (-1);
/* does the key exist? */
skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
if (skey == NULL)
return (-1);
/* remove it */
LIST_REMOVE(skey, next);
sctp_free_sharedkey(skey); /* frees skey->key as well */
/* clear any cached keys */
sctp_clear_cachedkeys(stcb, keyid);
return (0);
}
/*
* deletes a shared key from the endpoint ASSUMES INP_WLOCK is already held
*/
int
sctp_delete_sharedkey_ep(struct sctp_inpcb *inp, uint16_t keyid)
{
sctp_sharedkey_t *skey;
struct sctp_tcb *stcb;
if (inp == NULL)
return (-1);
/* is the keyid the active sending key on the endpoint or any assoc */
if (keyid == inp->sctp_ep.default_keyid)
return (-1);
LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
SCTP_TCB_LOCK(stcb);
if (keyid == stcb->asoc.authinfo.assoc_keyid) {
SCTP_TCB_UNLOCK(stcb);
return (-1);
}
SCTP_TCB_UNLOCK(stcb);
}
/* does the key exist? */
skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid);
if (skey == NULL)
return (-1);
/* remove it */
LIST_REMOVE(skey, next);
sctp_free_sharedkey(skey); /* frees skey->key as well */
/* clear any cached keys */
sctp_clear_cachedkeys_ep(inp, keyid);
return (0);
}
/*
* set the active key on an association ASSUME TCB_LOCK is already held
*/
int
sctp_auth_setactivekey(struct sctp_tcb *stcb, uint16_t keyid)
{
sctp_sharedkey_t *skey = NULL;
sctp_key_t *key = NULL;
int using_ep_key = 0;
/* find the key on the assoc */
skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
if (skey == NULL) {
/* if not on the assoc, find the key on the endpoint */
atomic_add_int(&stcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
atomic_add_int(&stcb->asoc.refcnt, -1);
skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys,
keyid);
using_ep_key = 1;
}
if (skey == NULL) {
/* that key doesn't exist */
if (using_ep_key) {
SCTP_INP_RUNLOCK(stcb->sctp_ep);
}
return (-1);
}
/* get the shared key text */
key = skey->key;
/* free any existing cached key */
if (stcb->asoc.authinfo.assoc_key != NULL)
sctp_free_key(stcb->asoc.authinfo.assoc_key);
/* compute a new assoc key and cache it */
stcb->asoc.authinfo.assoc_key =
sctp_compute_hashkey(stcb->asoc.authinfo.random,
stcb->asoc.authinfo.peer_random, key);
stcb->asoc.authinfo.assoc_keyid = keyid;
#ifdef SCTP_DEBUG
if (SCTP_AUTH_DEBUG)
sctp_print_key(stcb->asoc.authinfo.assoc_key, "Assoc Key");
#endif
if (using_ep_key) {
SCTP_INP_RUNLOCK(stcb->sctp_ep);
}
return (0);
}
/*
* set the active key on an endpoint ASSUMES INP_WLOCK is already held
*/
int
sctp_auth_setactivekey_ep(struct sctp_inpcb *inp, uint16_t keyid)
{
sctp_sharedkey_t *skey;
/* find the key */
skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid);
if (skey == NULL) {
/* that key doesn't exist */
return (-1);
}
inp->sctp_ep.default_keyid = keyid;
return (0);
}
/*
* get local authentication parameters from cookie (from INIT-ACK)
*/
void
sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m,
uint32_t offset, uint32_t length)
{
struct sctp_paramhdr *phdr, tmp_param;
uint16_t plen, ptype;
uint8_t random_store[SCTP_PARAM_BUFFER_SIZE];
struct sctp_auth_random *p_random = NULL;
uint16_t random_len = 0;
uint8_t hmacs_store[SCTP_PARAM_BUFFER_SIZE];
struct sctp_auth_hmac_algo *hmacs = NULL;
uint16_t hmacs_len = 0;
uint8_t chunks_store[SCTP_PARAM_BUFFER_SIZE];
struct sctp_auth_chunk_list *chunks = NULL;
uint16_t num_chunks = 0;
sctp_key_t *new_key;
uint32_t keylen;
/* convert to upper bound */
length += offset;
phdr = (struct sctp_paramhdr *)sctp_m_getptr(m, offset,
sizeof(struct sctp_paramhdr), (uint8_t *) & tmp_param);
while (phdr != NULL) {
ptype = ntohs(phdr->param_type);
plen = ntohs(phdr->param_length);
if ((plen == 0) || (offset + plen > length))
break;
if (ptype == SCTP_RANDOM) {
if (plen > sizeof(random_store))
break;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)random_store, min(plen, sizeof(random_store)));
if (phdr == NULL)
return;
/* save the random and length for the key */
p_random = (struct sctp_auth_random *)phdr;
random_len = plen - sizeof(*p_random);
} else if (ptype == SCTP_HMAC_LIST) {
int num_hmacs;
int i;
if (plen > sizeof(hmacs_store))
break;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)hmacs_store, min(plen, sizeof(hmacs_store)));
if (phdr == NULL)
return;
/* save the hmacs list and num for the key */
hmacs = (struct sctp_auth_hmac_algo *)phdr;
hmacs_len = plen - sizeof(*hmacs);
num_hmacs = hmacs_len / sizeof(hmacs->hmac_ids[0]);
if (stcb->asoc.local_hmacs != NULL)
sctp_free_hmaclist(stcb->asoc.local_hmacs);
stcb->asoc.local_hmacs = sctp_alloc_hmaclist(num_hmacs);
if (stcb->asoc.local_hmacs != NULL) {
for (i = 0; i < num_hmacs; i++) {
(void)sctp_auth_add_hmacid(stcb->asoc.local_hmacs,
ntohs(hmacs->hmac_ids[i]));
}
}
} else if (ptype == SCTP_CHUNK_LIST) {
int i;
if (plen > sizeof(chunks_store))
break;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)chunks_store, min(plen, sizeof(chunks_store)));
if (phdr == NULL)
return;
chunks = (struct sctp_auth_chunk_list *)phdr;
num_chunks = plen - sizeof(*chunks);
/* save chunks list and num for the key */
if (stcb->asoc.local_auth_chunks != NULL)
sctp_clear_chunklist(stcb->asoc.local_auth_chunks);
else
stcb->asoc.local_auth_chunks = sctp_alloc_chunklist();
for (i = 0; i < num_chunks; i++) {
(void)sctp_auth_add_chunk(chunks->chunk_types[i],
stcb->asoc.local_auth_chunks);
}
}
/* get next parameter */
offset += SCTP_SIZE32(plen);
if (offset + sizeof(struct sctp_paramhdr) > length)
break;
phdr = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_paramhdr),
(uint8_t *) & tmp_param);
}
/* concatenate the full random key */
#ifdef SCTP_AUTH_DRAFT_04
keylen = random_len;
new_key = sctp_alloc_key(keylen);
if (new_key != NULL) {
/* copy in the RANDOM */
if (p_random != NULL)
bcopy(p_random->random_data, new_key->key, random_len);
}
#else
keylen = sizeof(*p_random) + random_len + sizeof(*chunks) + num_chunks +
sizeof(*hmacs) + hmacs_len;
new_key = sctp_alloc_key(keylen);
if (new_key != NULL) {
/* copy in the RANDOM */
if (p_random != NULL) {
keylen = sizeof(*p_random) + random_len;
bcopy(p_random, new_key->key, keylen);
}
/* append in the AUTH chunks */
if (chunks != NULL) {
bcopy(chunks, new_key->key + keylen,
sizeof(*chunks) + num_chunks);
keylen += sizeof(*chunks) + num_chunks;
}
/* append in the HMACs */
if (hmacs != NULL) {
bcopy(hmacs, new_key->key + keylen,
sizeof(*hmacs) + hmacs_len);
}
}
#endif
if (stcb->asoc.authinfo.random != NULL)
sctp_free_key(stcb->asoc.authinfo.random);
stcb->asoc.authinfo.random = new_key;
stcb->asoc.authinfo.random_len = random_len;
#ifdef SCTP_AUTH_DRAFT_04
/* don't include the chunks and hmacs for draft -04 */
stcb->asoc.authinfo.random->keylen = random_len;
#endif
sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.assoc_keyid);
sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.recv_keyid);
/* negotiate what HMAC to use for the peer */
stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs,
stcb->asoc.local_hmacs);
/* copy defaults from the endpoint */
/* FIX ME: put in cookie? */
stcb->asoc.authinfo.assoc_keyid = stcb->sctp_ep->sctp_ep.default_keyid;
}
/*
* compute and fill in the HMAC digest for a packet
*/
void
sctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset,
struct sctp_auth_chunk *auth, struct sctp_tcb *stcb)
{
uint32_t digestlen;
sctp_sharedkey_t *skey;
sctp_key_t *key;
if ((stcb == NULL) || (auth == NULL))
return;
/* zero the digest + chunk padding */
digestlen = sctp_get_hmac_digest_len(stcb->asoc.peer_hmac_id);
bzero(auth->hmac, SCTP_SIZE32(digestlen));
/* is an assoc key cached? */
if (stcb->asoc.authinfo.assoc_key == NULL) {
skey = sctp_find_sharedkey(&stcb->asoc.shared_keys,
stcb->asoc.authinfo.assoc_keyid);
if (skey == NULL) {
/* not in the assoc list, so check the endpoint list */
skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys,
stcb->asoc.authinfo.assoc_keyid);
}
/* the only way skey is NULL is if null key id 0 is used */
if (skey != NULL)
key = skey->key;
else
key = NULL;
/* compute a new assoc key and cache it */
stcb->asoc.authinfo.assoc_key =
sctp_compute_hashkey(stcb->asoc.authinfo.random,
stcb->asoc.authinfo.peer_random, key);
SCTPDBG(SCTP_DEBUG_AUTH1, "caching key id %u\n",
stcb->asoc.authinfo.assoc_keyid);
#ifdef SCTP_DEBUG
if (SCTP_AUTH_DEBUG)
sctp_print_key(stcb->asoc.authinfo.assoc_key,
"Assoc Key");
#endif
}
/* set in the active key id */
auth->shared_key_id = htons(stcb->asoc.authinfo.assoc_keyid);
/* compute and fill in the digest */
(void)sctp_compute_hmac_m(stcb->asoc.peer_hmac_id,
stcb->asoc.authinfo.assoc_key,
m, auth_offset, auth->hmac);
}
static void
sctp_bzero_m(struct mbuf *m, uint32_t m_offset, uint32_t size)
{
struct mbuf *m_tmp;
uint8_t *data;
/* sanity check */
if (m == NULL)
return;
/* find the correct starting mbuf and offset (get start position) */
m_tmp = m;
while ((m_tmp != NULL) && (m_offset >= (uint32_t) SCTP_BUF_LEN(m_tmp))) {
m_offset -= SCTP_BUF_LEN(m_tmp);
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
/* now use the rest of the mbuf chain */
while ((m_tmp != NULL) && (size > 0)) {
data = mtod(m_tmp, uint8_t *) + m_offset;
if (size > (uint32_t) SCTP_BUF_LEN(m_tmp)) {
bzero(data, SCTP_BUF_LEN(m_tmp));
size -= SCTP_BUF_LEN(m_tmp);
} else {
bzero(data, size);
size = 0;
}
/* clear the offset since it's only for the first mbuf */
m_offset = 0;
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
}
/*
* process the incoming Authentication chunk return codes: -1 on any
* authentication error 0 on authentication verification
*/
int
sctp_handle_auth(struct sctp_tcb *stcb, struct sctp_auth_chunk *auth,
struct mbuf *m, uint32_t offset)
{
uint16_t chunklen;
uint16_t shared_key_id;
uint16_t hmac_id;
sctp_sharedkey_t *skey;
uint32_t digestlen;
uint8_t digest[SCTP_AUTH_DIGEST_LEN_MAX];
uint8_t computed_digest[SCTP_AUTH_DIGEST_LEN_MAX];
/* auth is checked for NULL by caller */
chunklen = ntohs(auth->ch.chunk_length);
if (chunklen < sizeof(*auth)) {
SCTP_STAT_INCR(sctps_recvauthfailed);
return (-1);
}
SCTP_STAT_INCR(sctps_recvauth);
/* get the auth params */
shared_key_id = ntohs(auth->shared_key_id);
hmac_id = ntohs(auth->hmac_id);
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP AUTH Chunk: shared key %u, HMAC id %u\n",
shared_key_id, hmac_id);
/* is the indicated HMAC supported? */
if (!sctp_auth_is_supported_hmac(stcb->asoc.local_hmacs, hmac_id)) {
struct mbuf *m_err;
struct sctp_auth_invalid_hmac *err;
SCTP_STAT_INCR(sctps_recvivalhmacid);
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP Auth: unsupported HMAC id %u\n",
hmac_id);
/*
* report this in an Error Chunk: Unsupported HMAC
* Identifier
*/
m_err = sctp_get_mbuf_for_msg(sizeof(*err), 0, M_DONTWAIT,
1, MT_HEADER);
if (m_err != NULL) {
/* pre-reserve some space */
SCTP_BUF_RESV_UF(m_err, sizeof(struct sctp_chunkhdr));
/* fill in the error */
err = mtod(m_err, struct sctp_auth_invalid_hmac *);
bzero(err, sizeof(*err));
err->ph.param_type = htons(SCTP_CAUSE_UNSUPPORTED_HMACID);
err->ph.param_length = htons(sizeof(*err));
err->hmac_id = ntohs(hmac_id);
SCTP_BUF_LEN(m_err) = sizeof(*err);
/* queue it */
sctp_queue_op_err(stcb, m_err);
}
return (-1);
}
/* get the indicated shared key, if available */
if ((stcb->asoc.authinfo.recv_key == NULL) ||
(stcb->asoc.authinfo.recv_keyid != shared_key_id)) {
/* find the shared key on the assoc first */
skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, shared_key_id);
if (skey == NULL) {
/* if not on the assoc, find it on the endpoint */
skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys,
shared_key_id);
}
/* if the shared key isn't found, discard the chunk */
if (skey == NULL) {
SCTP_STAT_INCR(sctps_recvivalkeyid);
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP Auth: unknown key id %u\n",
shared_key_id);
return (-1);
}
/* generate a notification if this is a new key id */
if (stcb->asoc.authinfo.recv_keyid != shared_key_id)
/*
* sctp_ulp_notify(SCTP_NOTIFY_AUTH_NEW_KEY, stcb,
* shared_key_id, (void
* *)stcb->asoc.authinfo.recv_keyid);
*/
sctp_notify_authentication(stcb, SCTP_AUTH_NEWKEY,
shared_key_id, stcb->asoc.authinfo.recv_keyid);
/* compute a new recv assoc key and cache it */
if (stcb->asoc.authinfo.recv_key != NULL)
sctp_free_key(stcb->asoc.authinfo.recv_key);
stcb->asoc.authinfo.recv_key =
sctp_compute_hashkey(stcb->asoc.authinfo.random,
stcb->asoc.authinfo.peer_random, skey->key);
stcb->asoc.authinfo.recv_keyid = shared_key_id;
#ifdef SCTP_DEBUG
if (SCTP_AUTH_DEBUG)
sctp_print_key(stcb->asoc.authinfo.recv_key, "Recv Key");
#endif
}
/* validate the digest length */
digestlen = sctp_get_hmac_digest_len(hmac_id);
if (chunklen < (sizeof(*auth) + digestlen)) {
/* invalid digest length */
SCTP_STAT_INCR(sctps_recvauthfailed);
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP Auth: chunk too short for HMAC\n");
return (-1);
}
/* save a copy of the digest, zero the pseudo header, and validate */
bcopy(auth->hmac, digest, digestlen);
sctp_bzero_m(m, offset + sizeof(*auth), SCTP_SIZE32(digestlen));
(void)sctp_compute_hmac_m(hmac_id, stcb->asoc.authinfo.recv_key,
m, offset, computed_digest);
/* compare the computed digest with the one in the AUTH chunk */
if (memcmp(digest, computed_digest, digestlen) != 0) {
SCTP_STAT_INCR(sctps_recvauthfailed);
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP Auth: HMAC digest check failed\n");
return (-1);
}
return (0);
}
/*
* Generate NOTIFICATION
*/
void
sctp_notify_authentication(struct sctp_tcb *stcb, uint32_t indication,
uint16_t keyid, uint16_t alt_keyid)
{
struct mbuf *m_notify;
struct sctp_authkey_event *auth;
struct sctp_queued_to_read *control;
if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTHEVNT))
/* event not enabled */
return;
m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_authkey_event),
0, M_DONTWAIT, 1, MT_HEADER);
if (m_notify == NULL)
/* no space left */
return;
SCTP_BUF_LEN(m_notify) = 0;
auth = mtod(m_notify, struct sctp_authkey_event *);
auth->auth_type = SCTP_AUTHENTICATION_EVENT;
auth->auth_flags = 0;
auth->auth_length = sizeof(*auth);
auth->auth_keynumber = keyid;
auth->auth_altkeynumber = alt_keyid;
auth->auth_indication = indication;
auth->auth_assoc_id = sctp_get_associd(stcb);
SCTP_BUF_LEN(m_notify) = sizeof(*auth);
SCTP_BUF_NEXT(m_notify) = NULL;
/* append to socket */
control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
0, 0, 0, 0, 0, 0, m_notify);
if (control == NULL) {
/* no memory */
sctp_m_freem(m_notify);
return;
}
control->spec_flags = M_NOTIFICATION;
control->length = SCTP_BUF_LEN(m_notify);
/* not that we need this */
control->tail_mbuf = m_notify;
sctp_add_to_readq(stcb->sctp_ep, stcb, control,
&stcb->sctp_socket->so_rcv, 1);
}
/*
* validates the AUTHentication related parameters in an INIT/INIT-ACK
* Note: currently only used for INIT as INIT-ACK is handled inline
* with sctp_load_addresses_from_init()
*/
int
sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit)
{
struct sctp_paramhdr *phdr, parm_buf;
uint16_t ptype, plen;
int peer_supports_asconf = 0;
int peer_supports_auth = 0;
int got_random = 0, got_hmacs = 0, got_chklist = 0;
uint8_t saw_asconf = 0;
uint8_t saw_asconf_ack = 0;
/* go through each of the params. */
phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf));
while (phdr) {
ptype = ntohs(phdr->param_type);
plen = ntohs(phdr->param_length);
if (offset + plen > limit) {
break;
}
if (plen < sizeof(struct sctp_paramhdr)) {
break;
}
if (ptype == SCTP_SUPPORTED_CHUNK_EXT) {
/* A supported extension chunk */
struct sctp_supported_chunk_types_param *pr_supported;
uint8_t local_store[SCTP_PARAM_BUFFER_SIZE];
int num_ent, i;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)&local_store, min(plen, sizeof(local_store)));
if (phdr == NULL) {
return (-1);
}
pr_supported = (struct sctp_supported_chunk_types_param *)phdr;
num_ent = plen - sizeof(struct sctp_paramhdr);
for (i = 0; i < num_ent; i++) {
switch (pr_supported->chunk_types[i]) {
case SCTP_ASCONF:
case SCTP_ASCONF_ACK:
peer_supports_asconf = 1;
break;
case SCTP_AUTHENTICATION:
peer_supports_auth = 1;
break;
default:
/* one we don't care about */
break;
}
}
} else if (ptype == SCTP_RANDOM) {
got_random = 1;
/* enforce the random length */
if (plen != (sizeof(struct sctp_auth_random) +
SCTP_AUTH_RANDOM_SIZE_REQUIRED)) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: invalid RANDOM len\n");
return (-1);
}
} else if (ptype == SCTP_HMAC_LIST) {
uint8_t store[SCTP_PARAM_BUFFER_SIZE];
struct sctp_auth_hmac_algo *hmacs;
int num_hmacs;
if (plen > sizeof(store))
break;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)store, min(plen, sizeof(store)));
if (phdr == NULL)
return (-1);
hmacs = (struct sctp_auth_hmac_algo *)phdr;
num_hmacs = (plen - sizeof(*hmacs)) /
sizeof(hmacs->hmac_ids[0]);
/* validate the hmac list */
if (sctp_verify_hmac_param(hmacs, num_hmacs)) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: invalid HMAC param\n");
return (-1);
}
got_hmacs = 1;
} else if (ptype == SCTP_CHUNK_LIST) {
int i, num_chunks;
uint8_t chunks_store[SCTP_SMALL_CHUNK_STORE];
/* did the peer send a non-empty chunk list? */
struct sctp_auth_chunk_list *chunks = NULL;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)chunks_store,
min(plen, sizeof(chunks_store)));
if (phdr == NULL)
return (-1);
/*-
* Flip through the list and mark that the
* peer supports asconf/asconf_ack.
*/
chunks = (struct sctp_auth_chunk_list *)phdr;
num_chunks = plen - sizeof(*chunks);
for (i = 0; i < num_chunks; i++) {
/* record asconf/asconf-ack if listed */
if (chunks->chunk_types[i] == SCTP_ASCONF)
saw_asconf = 1;
if (chunks->chunk_types[i] == SCTP_ASCONF_ACK)
saw_asconf_ack = 1;
}
if (num_chunks)
got_chklist = 1;
}
offset += SCTP_SIZE32(plen);
if (offset >= limit) {
break;
}
phdr = sctp_get_next_param(m, offset, &parm_buf,
sizeof(parm_buf));
}
/* validate authentication required parameters */
if (got_random && got_hmacs) {
peer_supports_auth = 1;
} else {
peer_supports_auth = 0;
}
if (!peer_supports_auth && got_chklist) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: peer sent chunk list w/o AUTH\n");
return (-1);
}
if (!sctp_asconf_auth_nochk && peer_supports_asconf &&
!peer_supports_auth) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"SCTP: peer supports ASCONF but not AUTH\n");
return (-1);
} else if ((peer_supports_asconf) && (peer_supports_auth) &&
((saw_asconf == 0) || (saw_asconf_ack == 0))) {
return (-2);
}
return (0);
}
void
sctp_initialize_auth_params(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
{
uint16_t chunks_len = 0;
uint16_t hmacs_len = 0;
uint16_t random_len = SCTP_AUTH_RANDOM_SIZE_DEFAULT;
sctp_key_t *new_key;
uint16_t keylen;
/* initialize hmac list from endpoint */
stcb->asoc.local_hmacs = sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
if (stcb->asoc.local_hmacs != NULL) {
hmacs_len = stcb->asoc.local_hmacs->num_algo *
sizeof(stcb->asoc.local_hmacs->hmac[0]);
}
/* initialize auth chunks list from endpoint */
stcb->asoc.local_auth_chunks =
sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
if (stcb->asoc.local_auth_chunks != NULL) {
int i;
for (i = 0; i < 256; i++) {
if (stcb->asoc.local_auth_chunks->chunks[i])
chunks_len++;
}
}
/* copy defaults from the endpoint */
stcb->asoc.authinfo.assoc_keyid = inp->sctp_ep.default_keyid;
/* now set the concatenated key (random + chunks + hmacs) */
#ifdef SCTP_AUTH_DRAFT_04
/* don't include the chunks and hmacs for draft -04 */
keylen = random_len;
new_key = sctp_generate_random_key(keylen);
#else
/* key includes parameter headers */
keylen = (3 * sizeof(struct sctp_paramhdr)) + random_len + chunks_len +
hmacs_len;
new_key = sctp_alloc_key(keylen);
if (new_key != NULL) {
struct sctp_paramhdr *ph;
int plen;
/* generate and copy in the RANDOM */
ph = (struct sctp_paramhdr *)new_key->key;
ph->param_type = htons(SCTP_RANDOM);
plen = sizeof(*ph) + random_len;
ph->param_length = htons(plen);
SCTP_READ_RANDOM(new_key->key + sizeof(*ph), random_len);
keylen = plen;
/* append in the AUTH chunks */
/* NOTE: currently we always have chunks to list */
ph = (struct sctp_paramhdr *)(new_key->key + keylen);
ph->param_type = htons(SCTP_CHUNK_LIST);
plen = sizeof(*ph) + chunks_len;
ph->param_length = htons(plen);
keylen += sizeof(*ph);
if (stcb->asoc.local_auth_chunks) {
int i;
for (i = 0; i < 256; i++) {
if (stcb->asoc.local_auth_chunks->chunks[i])
new_key->key[keylen++] = i;
}
}
/* append in the HMACs */
ph = (struct sctp_paramhdr *)(new_key->key + keylen);
ph->param_type = htons(SCTP_HMAC_LIST);
plen = sizeof(*ph) + hmacs_len;
ph->param_length = htons(plen);
keylen += sizeof(*ph);
(void)sctp_serialize_hmaclist(stcb->asoc.local_hmacs,
new_key->key + keylen);
}
#endif
if (stcb->asoc.authinfo.random != NULL)
sctp_free_key(stcb->asoc.authinfo.random);
stcb->asoc.authinfo.random = new_key;
stcb->asoc.authinfo.random_len = random_len;
}
#ifdef SCTP_HMAC_TEST
/*
* HMAC and key concatenation tests
*/
static void
sctp_print_digest(uint8_t * digest, uint32_t digestlen, const char *str)
{
uint32_t i;
printf("\n%s: 0x", str);
if (digest == NULL)
return;
for (i = 0; i < digestlen; i++)
printf("%02x", digest[i]);
}
static int
sctp_test_hmac(const char *str, uint16_t hmac_id, uint8_t * key,
uint32_t keylen, uint8_t * text, uint32_t textlen,
uint8_t * digest, uint32_t digestlen)
{
uint8_t computed_digest[SCTP_AUTH_DIGEST_LEN_MAX];
printf("\n%s:", str);
sctp_hmac(hmac_id, key, keylen, text, textlen, computed_digest);
sctp_print_digest(digest, digestlen, "Expected digest");
sctp_print_digest(computed_digest, digestlen, "Computed digest");
if (memcmp(digest, computed_digest, digestlen) != 0) {
printf("\nFAILED");
return (-1);
} else {
printf("\nPASSED");
return (0);
}
}
/*
* RFC 2202: HMAC-SHA1 test cases
*/
void
sctp_test_hmac_sha1(void)
{
uint8_t *digest;
uint8_t key[128];
uint32_t keylen;
uint8_t text[128];
uint32_t textlen;
uint32_t digestlen = 20;
int failed = 0;
/*
* test_case = 1 key =
* 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b key_len = 20
* data = "Hi There" data_len = 8 digest =
* 0xb617318655057264e28bc0b6fb378c8ef146be00
*/
keylen = 20;
memset(key, 0x0b, keylen);
textlen = 8;
strcpy(text, "Hi There");
digest = "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00";
if (sctp_test_hmac("SHA1 test case 1", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 2 key = "Jefe" key_len = 4 data =
* "what do ya want for nothing?" data_len = 28 digest =
* 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79
*/
keylen = 4;
strcpy(key, "Jefe");
textlen = 28;
strcpy(text, "what do ya want for nothing?");
digest = "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79";
if (sctp_test_hmac("SHA1 test case 2", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 3 key =
* 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa key_len = 20
* data = 0xdd repeated 50 times data_len = 50 digest
* = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3
*/
keylen = 20;
memset(key, 0xaa, keylen);
textlen = 50;
memset(text, 0xdd, textlen);
digest = "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3";
if (sctp_test_hmac("SHA1 test case 3", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 4 key =
* 0x0102030405060708090a0b0c0d0e0f10111213141516171819 key_len = 25
* data = 0xcd repeated 50 times data_len = 50 digest
* = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da
*/
keylen = 25;
memcpy(key, "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", keylen);
textlen = 50;
memset(text, 0xcd, textlen);
digest = "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda";
if (sctp_test_hmac("SHA1 test case 4", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 5 key =
* 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c key_len = 20
* data = "Test With Truncation" data_len = 20 digest
* = 0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04 digest-96 =
* 0x4c1a03424b55e07fe7f27be1
*/
keylen = 20;
memset(key, 0x0c, keylen);
textlen = 20;
strcpy(text, "Test With Truncation");
digest = "\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04";
if (sctp_test_hmac("SHA1 test case 5", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 6 key = 0xaa repeated 80 times key_len
* = 80 data = "Test Using Larger Than Block-Size Key -
* Hash Key First" data_len = 54 digest =
* 0xaa4ae5e15272d00e95705637ce8a3b55ed402112
*/
keylen = 80;
memset(key, 0xaa, keylen);
textlen = 54;
strcpy(text, "Test Using Larger Than Block-Size Key - Hash Key First");
digest = "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12";
if (sctp_test_hmac("SHA1 test case 6", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 7 key = 0xaa repeated 80 times key_len
* = 80 data = "Test Using Larger Than Block-Size Key and
* Larger Than One Block-Size Data" data_len = 73 digest =
* 0xe8e99d0f45237d786d6bbaa7965c7808bbff1a91
*/
keylen = 80;
memset(key, 0xaa, keylen);
textlen = 73;
strcpy(text, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
digest = "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91";
if (sctp_test_hmac("SHA1 test case 7", SCTP_AUTH_HMAC_ID_SHA1, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/* done with all tests */
if (failed)
printf("\nSHA1 test results: %d cases failed", failed);
else
printf("\nSHA1 test results: all test cases passed");
}
/*
* RFC 2202: HMAC-MD5 test cases
*/
void
sctp_test_hmac_md5(void)
{
uint8_t *digest;
uint8_t key[128];
uint32_t keylen;
uint8_t text[128];
uint32_t textlen;
uint32_t digestlen = 16;
int failed = 0;
/*
* test_case = 1 key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
* key_len = 16 data = "Hi There" data_len = 8 digest =
* 0x9294727a3638bb1c13f48ef8158bfc9d
*/
keylen = 16;
memset(key, 0x0b, keylen);
textlen = 8;
strcpy(text, "Hi There");
digest = "\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d";
if (sctp_test_hmac("MD5 test case 1", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 2 key = "Jefe" key_len = 4 data =
* "what do ya want for nothing?" data_len = 28 digest =
* 0x750c783e6ab0b503eaa86e310a5db738
*/
keylen = 4;
strcpy(key, "Jefe");
textlen = 28;
strcpy(text, "what do ya want for nothing?");
digest = "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38";
if (sctp_test_hmac("MD5 test case 2", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 3 key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* key_len = 16 data = 0xdd repeated 50 times data_len = 50
* digest = 0x56be34521d144c88dbb8c733f0e8b3f6
*/
keylen = 16;
memset(key, 0xaa, keylen);
textlen = 50;
memset(text, 0xdd, textlen);
digest = "\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6";
if (sctp_test_hmac("MD5 test case 3", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 4 key =
* 0x0102030405060708090a0b0c0d0e0f10111213141516171819 key_len = 25
* data = 0xcd repeated 50 times data_len = 50 digest
* = 0x697eaf0aca3a3aea3a75164746ffaa79
*/
keylen = 25;
memcpy(key, "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", keylen);
textlen = 50;
memset(text, 0xcd, textlen);
digest = "\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79";
if (sctp_test_hmac("MD5 test case 4", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 5 key = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
* key_len = 16 data = "Test With Truncation" data_len = 20
* digest = 0x56461ef2342edc00f9bab995690efd4c digest-96
* 0x56461ef2342edc00f9bab995
*/
keylen = 16;
memset(key, 0x0c, keylen);
textlen = 20;
strcpy(text, "Test With Truncation");
digest = "\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c";
if (sctp_test_hmac("MD5 test case 5", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 6 key = 0xaa repeated 80 times key_len
* = 80 data = "Test Using Larger Than Block-Size Key -
* Hash Key First" data_len = 54 digest =
* 0x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd
*/
keylen = 80;
memset(key, 0xaa, keylen);
textlen = 54;
strcpy(text, "Test Using Larger Than Block-Size Key - Hash Key First");
digest = "\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd";
if (sctp_test_hmac("MD5 test case 6", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/*
* test_case = 7 key = 0xaa repeated 80 times key_len
* = 80 data = "Test Using Larger Than Block-Size Key and
* Larger Than One Block-Size Data" data_len = 73 digest =
* 0x6f630fad67cda0ee1fb1f562db3aa53e
*/
keylen = 80;
memset(key, 0xaa, keylen);
textlen = 73;
strcpy(text, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
digest = "\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e";
if (sctp_test_hmac("MD5 test case 7", SCTP_AUTH_HMAC_ID_MD5, key, keylen,
text, textlen, digest, digestlen) < 0)
failed++;
/* done with all tests */
if (failed)
printf("\nMD5 test results: %d cases failed", failed);
else
printf("\nMD5 test results: all test cases passed");
}
/*
* test assoc key concatenation
*/
static int
sctp_test_key_concatenation(sctp_key_t * key1, sctp_key_t * key2,
sctp_key_t * expected_key)
{
sctp_key_t *key;
int ret_val;
sctp_show_key(key1, "\nkey1");
sctp_show_key(key2, "\nkey2");
key = sctp_compute_hashkey(key1, key2, NULL);
sctp_show_key(expected_key, "\nExpected");
sctp_show_key(key, "\nComputed");
if (memcmp(key, expected_key, expected_key->keylen) != 0) {
printf("\nFAILED");
ret_val = -1;
} else {
printf("\nPASSED");
ret_val = 0;
}
sctp_free_key(key1);
sctp_free_key(key2);
sctp_free_key(expected_key);
sctp_free_key(key);
return (ret_val);
}
void
sctp_test_authkey(void)
{
sctp_key_t *key1, *key2, *expected_key;
int failed = 0;
/* test case 1 */
key1 = sctp_set_key("\x01\x01\x01\x01", 4);
key2 = sctp_set_key("\x01\x02\x03\x04", 4);
expected_key = sctp_set_key("\x01\x01\x01\x01\x01\x02\x03\x04", 8);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 2 */
key1 = sctp_set_key("\x00\x00\x00\x01", 4);
key2 = sctp_set_key("\x02", 1);
expected_key = sctp_set_key("\x00\x00\x00\x01\x02", 5);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 3 */
key1 = sctp_set_key("\x01", 1);
key2 = sctp_set_key("\x00\x00\x00\x02", 4);
expected_key = sctp_set_key("\x01\x00\x00\x00\x02", 5);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 4 */
key1 = sctp_set_key("\x00\x00\x00\x01", 4);
key2 = sctp_set_key("\x01", 1);
expected_key = sctp_set_key("\x01\x00\x00\x00\x01", 5);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 5 */
key1 = sctp_set_key("\x01", 1);
key2 = sctp_set_key("\x00\x00\x00\x01", 4);
expected_key = sctp_set_key("\x01\x00\x00\x00\x01", 5);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 6 */
key1 = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07", 11);
key2 = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x08", 11);
expected_key = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x08", 22);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* test case 7 */
key1 = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x08", 11);
key2 = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07", 11);
expected_key = sctp_set_key("\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x08", 22);
if (sctp_test_key_concatenation(key1, key2, expected_key) < 0)
failed++;
/* done with all tests */
if (failed)
printf("\nKey concatenation test results: %d cases failed", failed);
else
printf("\nKey concatenation test results: all test cases passed");
}
#if defined(STANDALONE_HMAC_TEST)
int
main(void)
{
sctp_test_hmac_sha1();
sctp_test_hmac_md5();
sctp_test_authkey();
}
#endif /* STANDALONE_HMAC_TEST */
#endif /* SCTP_HMAC_TEST */