Build radsecproxy-hash.
[libradsec.git] / fticks_hashmac.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <ctype.h>
6 #include <nettle/sha.h>
7 #include <nettle/hmac.h>
8 #include "fticks_hashmac.h"
9
10 static void
11 _format_hash(const uint8_t *hash, size_t out_len, uint8_t *out)
12 {
13     int ir, iw;
14
15     for (ir = 0, iw = 0; iw <= out_len - 3; ir++, iw += 2)
16         sprintf((char *) out + iw, "%02x", hash[ir % SHA256_DIGEST_SIZE]);
17 }
18
19 static void
20 _hash(const uint8_t *in,
21       const uint8_t *key,
22       size_t out_len,
23       uint8_t *out)
24 {
25     if (key == NULL) {
26         struct sha256_ctx ctx;
27         uint8_t hash[SHA256_DIGEST_SIZE];
28
29         sha256_init(&ctx);
30         sha256_update(&ctx, strlen((char *) in), in);
31         sha256_digest(&ctx, sizeof(hash), hash);
32         _format_hash(hash, out_len, out);
33     }
34     else {
35         struct hmac_sha256_ctx ctx;
36         uint8_t hash[SHA256_DIGEST_SIZE];
37
38         hmac_sha256_set_key(&ctx, strlen((char *) key), key);
39         hmac_sha256_update(&ctx, strlen((char *) in), in);
40         hmac_sha256_digest(&ctx, sizeof(hash), hash);
41         _format_hash(hash, out_len, out);
42     }
43 }
44
45 /** Hash the Ethernet MAC address in \a IN, keying a HMAC with \a KEY
46     unless \a KEY is NULL.  If \a KEY is null \a IN is hashed with an
47     ordinary cryptographic hash function such as SHA-2.
48
49     \a IN and \a KEY are NULL terminated strings.
50
51     \a IN is supposed to be an Ethernet MAC address and is sanitised
52     by lowercasing it, removing all but [0-9a-f] and truncating it at
53     the first ';' found.  The truncation is done because RADIUS
54     supposedly has a praxis of tacking on SSID to the MAC address in
55     Calling-Station-Id.
56
57     \return 0 on success, -ENOMEM on out of memory.
58 */
59 int
60 fticks_hashmac(const uint8_t *in,
61                const uint8_t *key,
62                size_t out_len,
63                uint8_t *out)
64 {
65     uint8_t *in_copy = NULL;
66     uint8_t *p = NULL;
67     int i;
68
69     in_copy = calloc(1, strlen((const char *) in) + 1);
70     if (in_copy == NULL)
71         return -ENOMEM;
72
73     /* Sanitise and lowercase 'in' into 'in_copy'.  */
74     for (i = 0, p = in_copy; in[i] != '\0'; i++) {
75         if (in[i] == ';') {
76             *p++ = '\0';
77             break;
78         }
79         if (in[i] >= '0' && in[i] <= '9') {
80             *p++ = in[i];
81         }
82         else if (tolower(in[i]) >= 'a' && tolower(in[i]) <= 'f') {
83             *p++ = tolower(in[i]);
84         }
85     }
86
87     _hash(in_copy, key, out_len, out);
88     free(in_copy);
89     return 0;
90 }