root/sys/netinet/sctp_hashdriver.c
/*      $KAME: sctp_hashdriver.c,v 1.6 2004/02/24 21:52:26 itojun Exp $ */
/*      $NetBSD: sctp_hashdriver.c,v 1.1 2015/10/13 21:28:35 rjs Exp $  */

/*
 * Copyright (c) 2001, 2002, 2003, 2004 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:
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Cisco Systems, Inc.
 * 4. Neither the name of the project 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 CISCO SYSTEMS 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 CISCO SYSTEMS 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>
__KERNEL_RCSID(0, "$NetBSD: sctp_hashdriver.c,v 1.1 2015/10/13 21:28:35 rjs Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <netinet/sctp_constants.h>
#ifdef USE_MD5
#include <sys/md5.h>
#else
#include <sys/sha1.h>
#endif
#include <netinet/sctp_hashdriver.h>

/*
 * Main driver for SCTP's hashing.
 * passing a two pointers and two lengths, returning a digest pointer
 * filled. The md5 code was taken directly from the RFC (2104) so to
 * understand it you may want to go look at the RFC referenced in the
 * SCTP spec. We did modify this code to either user OURs implementation
 * of SLA1 or the MD5 that comes from its RFC. SLA1 may have IPR issues
 * so you need to check in to this if you wish to use it... Or at least
 * that is what the FIP-180.1 web page says.
 */

void sctp_hash_digest(char *key, int key_len, char *text, int text_len,
    unsigned char *digest)
{
#ifdef USE_MD5
        MD5Context context;
#else
        SHA1_CTX context;
#endif /* USE_MD5 */
        /* inner padding - key XORd with ipad */
        unsigned char k_ipad[65];
        /* outer padding - key XORd with opad */
        unsigned char k_opad[65];
        unsigned char tk[20];
        int i;

        if (key_len > 64) {
#ifdef USE_MD5
                md5_ctxt tctx;
                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(tk, &tctx);
                key = tk;
                key_len = 16;
#else
                SHA1_CTX tctx;
                SHA1Init(&tctx);
                SHA1Update(&tctx, key, key_len);
                SHA1Final(tk, &tctx);
                key = tk;
                key_len = 20;
#endif /* USE_MD5 */
        }

        /*
         * 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
         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */
        memset(k_ipad, 0, sizeof k_ipad);
        memset(k_opad, 0, 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
         */
#ifdef USE_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 */
#else
        SHA1Init(&context);                     /* init context for 1st pass */
        SHA1Update(&context, k_ipad, 64);       /* start with inner pad */
        SHA1Update(&context, text, text_len);   /* then text of datagram */
        SHA1Final(digest, &context);            /* finish up 1st pass */
#endif /* USE_MD5 */

        /*
         * perform outer MD5
         */
#ifdef USE_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 */
#else
        SHA1Init(&context);                     /* init context for 2nd pass */
        SHA1Update(&context, k_opad, 64);       /* start with outer pad */
        SHA1Update(&context, digest, 20);       /* then results of 1st hash */
        SHA1Final(digest, &context);            /* finish up 2nd pass */
#endif /* USE_MD5 */
}

void sctp_hash_digest_m(char *key, int key_len, struct mbuf *m, int offset,
    unsigned char *digest)
{
        struct mbuf *m_at;
#ifdef USE_MD5
        MD5Context context;
#else
        SHA1_CTX context;
#endif /* USE_MD5 */
        /* inner padding - key XORd with ipad */
        unsigned char k_ipad[65];
        /* outer padding - key XORd with opad */
        unsigned char k_opad[65];
        unsigned char tk[20];
        int i;

        if (key_len > 64) {
#ifdef USE_MD5
                MD5Context tctx;
                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(tk, &tctx);
                key = tk;
                key_len = 16;
#else
                SHA1_CTX tctx;
                SHA1Init(&tctx);
                SHA1Update(&tctx, key, key_len);
                SHA1Final(tk, &tctx);
                key = tk;
                key_len = 20;
#endif /* USE_MD5 */
        }

        /*
         * 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
         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */
        memset(k_ipad, 0, sizeof k_ipad);
        memset(k_opad, 0, 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;
        }

        /* find the correct mbuf and offset into mbuf */
        m_at = m;
        while ((m_at != NULL) && (offset > m_at->m_len)) {
                offset -= m_at->m_len;  /* update remaining offset left */
                m_at = m_at->m_next;
        }
        /*
         * perform inner MD5
         */
#ifdef USE_MD5
        MD5Init(&context);                      /* init context for 1st pass */
        MD5Update(&context, k_ipad, 64);        /* start with inner pad */
        /******/
        while (m_at != NULL) {
                /* then text of datagram... */
                MD5Update(&context, mtod(m_at, char *)+offset,
                          m_at->m_len-offset);
                /* only offset on the first mbuf */
                offset = 0;
                m_at = m_at->m_next;
        }
        /******/
        MD5Final(digest, &context);             /* finish up 1st pass */
#else
        SHA1Init(&context);                     /* init context for 1st pass */
        SHA1Update(&context, k_ipad, 64);       /* start with inner pad */
        /******/
        while (m_at != NULL) {
                /* then text of datagram */
                SHA1Update(&context, mtod(m_at, char *)+offset,
                             m_at->m_len-offset);
                /* only offset on the first mbuf */
                offset = 0;
                m_at = m_at->m_next;
        }
        /******/
        SHA1Final(digest, &context);             /* finish up 1st pass */
#endif /* USE_MD5 */

        /*
         * perform outer MD5
         */
#ifdef USE_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 */
#else
        SHA1Init(&context);                     /* init context for 2nd pass */
        SHA1Update(&context, k_opad, 64);       /* start with outer pad */
        SHA1Update(&context, digest, 20);       /* then results of 1st hash */
        SHA1Final(digest, &context);            /* finish up 2nd pass */
#endif /* USE_MD5 */
}