blob: e7854ad7e1feae7bd0f3d2aedaefff7debcb56bd [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <openssl/md5.h>
#include <openssl/hmac.h>
const char SOFT[] = "AEIOUY" "V";
const char HARD[] = "BCDFGHJKLMNPQRSTVWXYZ" "AEIOU";
const char *consensus_key_file = "/tmp/waveguide/consensus_key";
#define CONSENSUS_KEY_LEN 16
uint8_t consensus_key[CONSENSUS_KEY_LEN] = {0};
#define MAC_ADDR_LEN 17
void default_consensus_key()
{
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
ssize_t siz = sizeof(consensus_key);
if (read(fd, consensus_key, siz) != siz) {
/* https://xkcd.com/221/ */
memset(consensus_key, time(NULL), siz);
}
close(fd);
}
}
/* Read the waveguide consensus_key, if any. Returns 0 if
* a key was present, 1 if not or something fails. */
int get_consensus_key()
{
int fd, rc = 1;
uint8_t new_key[sizeof(consensus_key)];
if ((fd = open(consensus_key_file, O_RDONLY)) < 0) {
return 1;
}
if (read(fd, new_key, sizeof(new_key)) == sizeof(new_key)) {
memcpy(consensus_key, new_key, sizeof(consensus_key));
rc = 0;
}
close(fd);
return rc;
}
/* Given a value from 0..4095, encode it as a cons+vowel+cons sequence. */
void trigraph(int num, char *out)
{
int ns = sizeof(SOFT) - 1;
int nh = sizeof(HARD) - 1;
int c1, c2, c3;
c3 = num % nh;
c2 = (num / nh) % ns;
c1 = num / nh / ns;
out[0] = HARD[c1];
out[1] = SOFT[c2];
out[2] = HARD[c3];
}
int hex_chr_to_int(char hex) {
switch(hex) {
case '0' ... '9':
return hex - '0';
case 'a' ... 'f':
return hex - 'a' + 10;
case 'A' ... 'F':
return hex - 'A' + 10;
}
return 0;
}
/*
* Convert a string of the form "00:11:22:33:44:55" to
* a binary array 001122334455.
*/
void get_binary_mac(const char *mac, uint8_t *out) {
int i;
for (i = 0; i < MAC_ADDR_LEN; i += 3) {
*out = (hex_chr_to_int(mac[i]) << 4) | hex_chr_to_int(mac[i+1]);
out++;
}
}
void get_anonid_for_mac(const char *mac, char *out) {
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int digest_len = sizeof(digest);
uint8_t macbin[6];
uint32_t num;
get_binary_mac(mac, macbin);
HMAC(EVP_md5(), consensus_key, CONSENSUS_KEY_LEN, macbin, sizeof(macbin),
digest, &digest_len);
num = (digest[0] << 16) | (digest[1] << 8) | digest[2];
trigraph((num >> 12) & 0x0fff, out);
trigraph((num ) & 0x0fff, out + 3);
}
void usage(const char *progname)
{
fprintf(stderr, "usage: %s: -a ##:##:##:##:##:## [-k consensus_key]\n",
progname);
fprintf(stderr, "\t-a addr: MAC address to generate an anonid for\n");
fprintf(stderr, "\t-k key: Use a specific consensus_key. "
"Default is to read it from %s\n", consensus_key_file);
exit(1);
}
int main(int argc, char **argv)
{
struct option long_options[] = {
{"addr", required_argument, 0, 'a'},
{"consensus_key", required_argument, 0, 'k'},
{0, 0, 0, 0},
};
const char *addr = NULL;
char anonid[7];
size_t lim;
int c;
setlinebuf(stdout);
alarm(30);
if (get_consensus_key()) {
default_consensus_key();
}
while ((c = getopt_long(argc, argv, "a:k:", long_options, NULL)) != -1) {
switch (c) {
case 'a':
addr = optarg;
break;
case 'k':
lim = (sizeof(consensus_key) > strlen(optarg)) ? strlen(optarg) :
sizeof(consensus_key);
memset(consensus_key, 0, sizeof(consensus_key));
memcpy(consensus_key, optarg, lim);
break;
default:
usage(argv[0]);
break;
}
}
if (addr == NULL) {
usage(argv[0]);
}
get_anonid_for_mac(addr, anonid);
printf("%s\n", anonid);
exit(0);
}