Browse Source

oauth token encode and decode utility

Mészáros Mihály 9 years ago
parent
commit
d023a40b59
3 changed files with 473 additions and 1 deletions
  1. 6 1
      Makefile.in
  2. 452 0
      src/apps/oauth/oauth.c
  3. 15 0
      src/apps/oauth/test.sh

+ 6 - 1
Makefile.in

@@ -34,7 +34,7 @@ SERVERAPP_HEADERS = src/apps/relay/userdb.h src/apps/relay/tls_listener.h src/ap
 SERVERAPP_MODS = src/apps/relay/mainrelay.c src/apps/relay/netengine.c src/apps/relay/libtelnet.c src/apps/relay/turn_admin_server.c src/apps/relay/userdb.c src/apps/relay/tls_listener.c src/apps/relay/dtls_listener.c ${HIREDIS_MODS} ${USERDB_MODS}
 SERVERAPP_DEPS = ${SERVERTURN_MODS} ${SERVERTURN_DEPS} ${SERVERAPP_MODS} ${SERVERAPP_HEADERS} ${COMMON_DEPS} ${IMPL_DEPS} lib/libturnclient.a
 
-TURN_BUILD_RESULTS = bin/turnutils_natdiscovery bin/turnutils_stunclient bin/turnutils_rfc5769check bin/turnutils_uclient bin/turnserver bin/turnutils_peer lib/libturnclient.a include/turn/ns_turn_defs.h sqlite_empty_db
+TURN_BUILD_RESULTS = bin/turnutils_oauth bin/turnutils_natdiscovery bin/turnutils_stunclient bin/turnutils_rfc5769check bin/turnutils_uclient bin/turnserver bin/turnutils_peer lib/libturnclient.a include/turn/ns_turn_defs.h sqlite_empty_db
 
 all:	${TURN_BUILD_RESULTS}
 
@@ -59,6 +59,11 @@ bin/turnutils_natdiscovery:	${COMMON_DEPS} lib/libturnclient.a src/apps/natdisco
 	${MKBUILDDIR} bin
 	${CC} ${CPPFLAGS} ${CFLAGS} src/apps/natdiscovery/natdiscovery.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS}
 
+bin/turnutils_oauth:	${COMMON_DEPS} lib/libturnclient.a src/apps/oauth/oauth.c
+	pwd
+	${MKBUILDDIR} bin
+	${CC} ${CPPFLAGS} ${CFLAGS} src/apps/oauth/oauth.c ${COMMON_MODS} -o $@ -Llib -lturnclient -Llib ${LDFLAGS}
+
 bin/turnutils_stunclient:	${COMMON_DEPS} lib/libturnclient.a src/apps/stunclient/stunclient.c 
 	pwd
 	${MKBUILDDIR} bin

+ 452 - 0
src/apps/oauth/oauth.c

@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2011, 2012, 2013 Citrix Systems
+ *
+ * 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. 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 THE PROJECT 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 PROJECT 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 <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#include "ns_turn_utils.h"
+#include "apputils.h"
+#include "stun_buffer.h"
+
+////////////////////////////////////////////////////
+
+#define OAUTH_TOKEN_SIZE 1000 //TODO: find insted of 1000 the real max of encoded token length 
+#define OAUTH_MAC_KEY_SIZE 32
+#define OAUTH_LTK_ID_SIZE 32
+#define OAUTH_LTK_SIZE 32
+#define OAUTH_LTK_BASE64ENCODED_SIZE 44
+#define OAUTH_TOKEN_LIFETIME 3600
+#define OAUTH_AS_RS_ALG_SIZE 7 
+#define OAUTH_SERVER_NAME_SIZE 255 
+#define OAUTH_GCM_NONCE_BASE64ENCODED_SIZE 16 
+#define OAUTH_HMAC_ALG_SIZE 20
+
+
+static int setup_ikm_key(const char *kid, 
+                        const char *ikm_key, 
+                        const turn_time_t key_timestamp, 
+                        const turn_time_t key_lifetime, 
+                        const char *as_rs_alg, 
+                        oauth_key *key) { 
+
+        ns_bzero(key,sizeof(*key));
+
+        oauth_key_data okd;
+        ns_bzero(&okd,sizeof(okd));
+
+        {
+                oauth_key_data_raw okdr;
+                ns_bzero(&okdr,sizeof(okdr));
+
+                STRCPY(okdr.kid,kid);
+                STRCPY(okdr.ikm_key,ikm_key);
+                STRCPY(okdr.as_rs_alg,as_rs_alg);
+                okdr.timestamp = key_timestamp;
+                okdr.lifetime = key_lifetime;
+
+                convert_oauth_key_data_raw(&okdr, &okd);
+        }        
+
+        char err_msg[1025] = "\0";
+        size_t err_msg_size = sizeof(err_msg) - 1;
+
+        if (convert_oauth_key_data(&okd, key, err_msg, err_msg_size) < 0) {
+                        fprintf(stderr, "%s\n", err_msg);
+                        return -1;
+        }         
+
+        return 0;
+}
+
+
+
+static int encode_token(const char* server_name,
+                        const char* gcm_nonce,
+                        const char* mac_key,
+                        const uint64_t token_timestamp,
+                        const uint32_t token_lifetime,
+                        const oauth_key key,
+                        char* base64encoded_etoken) {
+
+
+        oauth_token ot;
+        ns_bzero(&ot,sizeof(ot));
+
+        const size_t mac_key_length=strlen(mac_key);
+        ot.enc_block.key_length = (uint16_t)mac_key_length;
+        STRCPY(ot.enc_block.mac_key,mac_key);
+        ot.enc_block.timestamp = token_timestamp;
+        ot.enc_block.lifetime = token_lifetime;
+
+        encoded_oauth_token etoken;
+        ns_bzero(&etoken,sizeof(etoken));
+
+        if (encode_oauth_token((const u08bits *) server_name, &etoken, &key, &ot, (const u08bits*)gcm_nonce) < 0) {
+                fprintf(stderr, "%s: cannot encode oauth token\n",
+                                __FUNCTION__);
+                return -1;
+        }
+
+        size_t base64encoded_etoken_length;
+        const char *tmp=base64_encode((unsigned char *)(etoken.token), etoken.size, &base64encoded_etoken_length);
+        STRCPY(base64encoded_etoken,tmp);
+
+        return 0;
+}
+
+static int validate_decode_token(const char* server_name, 
+                        const oauth_key key,
+                        const char* base64encoded_etoken, oauth_token* dot) {
+
+        
+        ns_bzero((dot),sizeof(*dot));
+
+        encoded_oauth_token etoken;
+        ns_bzero(&etoken,sizeof(etoken));
+
+        const size_t base64encoded_etoken_length=strlen(base64encoded_etoken);
+        const unsigned char *tmp = base64_decode(base64encoded_etoken,base64encoded_etoken_length,&etoken.size);
+        memcpy(etoken.token,tmp,etoken.size);
+                        
+        if (decode_oauth_token((const u08bits *) server_name, &etoken, &key, dot) < 0) {
+                fprintf(stderr, "%s: cannot decode oauth token\n",
+                                __FUNCTION__);
+                return -1;
+        } else {
+                return 0;
+        };
+}
+
+static void print_token_body(oauth_token* dot) {
+        printf("\n");
+        printf("Token encrpyted body:\n");
+        printf("{\n");
+        printf("    mac key: %s\n", (char*) dot->enc_block.mac_key);
+        printf("    mac key length: %d\n", (int) dot->enc_block.key_length);
+        time_t time=dot->enc_block.timestamp>>16;
+        unsigned msec=(dot->enc_block.timestamp & 0xFFFF)*64;
+        printf("    timestamp:\n");
+        printf("        unixtime: %s", ctime(&time));
+        printf("        msec:%u\n", msec);
+        printf("    lifetime: %lu\n", (unsigned long) dot->enc_block.lifetime);
+        printf("}\n");
+}
+
+//////////////// local definitions /////////////////
+
+const char Usage[] =
+  "Usage: oauth [ -e / -d ] [options]\n"
+  "Options:\n"
+  "\n"
+  "        -h, --help                       usage\n\n"
+  "        -v, --verbose                    verbose mode\n\n"
+  "        -e, --encrypt                    encrypt token\n"
+  "        -d, --decrypt                    decrypt validate token\n\n"
+  "        -i, --server-name                server name (max. 255 char)\n"
+  "        -j, --long-term-key-id           long term key id (max. 32 char)\n"
+  "        -k, --long-term-key              base64 encoded long term key\n"
+  "        -l  --long-term-key-timestamp    long term key timestamp (sec since epoch)\n"
+  "        -m, --long-term-key-lifetime     long term key lifetime in sec\n"
+  "        -n, --long-term-key-as-rs-alg    Authorization Server Resource Server encryption algorithm\n"
+  "        -o, --token-nonce                base64 encoded nonce base64(12 octet) = 16 char\n"
+  "        -p, --token-mac-key              base64 encoded MAC key base64(32 octet) = 44 char\n"
+  "        -q, --token-timestamp            timestamp in sec (sec since epoch, Default: actual gmtime)\n"
+  "        -r, --token-lifetime             lifetime in sec (Default: 3600)\n"
+  "        -t, --token                      base64 encoded encrypted token for validation and decryption\n"
+  "        -u, --hmac-alg                   stun client hmac algorithm\n";
+
+//////////////////////////////////////////////////
+
+
+int main(int argc, char **argv)
+{
+
+  oauth_key key;
+
+  //init vars with default values
+  char gcm_nonce[OAUTH_GCM_NONCE_SIZE+1]="";
+  char mac_key[OAUTH_MAC_KEY_SIZE+1]="";
+  uint64_t token_timestamp = (unsigned long long)time(NULL) << 16;
+  uint32_t token_lifetime = OAUTH_TOKEN_LIFETIME;
+ 
+  //oauth_key
+  char kid[OAUTH_LTK_ID_SIZE+1] = "";
+  char base64encoded_ltk[OAUTH_LTK_BASE64ENCODED_SIZE+1]="";
+  turn_time_t key_timestamp = 0;
+  turn_time_t key_lifetime = 0;
+  char as_rs_alg[OAUTH_AS_RS_ALG_SIZE+1]="A256GCM";
+  char server_name[OAUTH_SERVER_NAME_SIZE+1] = "";
+
+  char base64encoded_etoken[OAUTH_TOKEN_SIZE]=""; 
+
+  //TODO: replace SHA1 with an option. Actualy both big browser chrome and mozilla supports AFAIU implemented only SHA1.
+  char hmac_alg[OAUTH_HMAC_ALG_SIZE+1]="HMAC-SHA1";
+
+  static int verbose_flag=0;
+  static int encrypt_flag=0;
+  static int decrypt_flag=0;
+
+  static struct option long_options[] =
+   {
+     /* These options set a flag. */
+     {"verbose",                  no_argument,       &verbose_flag, 1},
+     {"encrypt",                  no_argument,       &encrypt_flag, 1},
+     {"decrypt",                  no_argument,       &decrypt_flag, 1},
+     {"help",                     no_argument,       0, 'h'},
+     {"server-name",              required_argument, 0, 'i'},
+     {"long-term-key-id",         required_argument, 0, 'j'},
+     {"long-term-key",            required_argument, 0, 'k'},
+     {"long-term-key-timestamp",  required_argument, 0, 'l'},
+     {"long-term-key-lifetime",   required_argument, 0, 'm'},
+     {"long-term-key-as-rs-alg",  required_argument, 0, 'n'},
+     {"token-nonce",              required_argument, 0, 'o'},
+     {"token-mac-key",            required_argument, 0, 'p'},
+     {"token-timestamp",          required_argument, 0, 'q'},
+     {"token-lifetime",           required_argument, 0, 'r'},
+     {"token",                    required_argument, 0, 't'},
+     {"hmac-alg",                 required_argument, 0, 'u'},
+     {0, 0, 0, 0}
+   };
+  /* getopt_long stores the option index here. */
+  int option_index = 0;
+
+  //tmp vars
+  size_t nonce_size=0;
+  char *nonce_val;
+
+  size_t mac_key_size;
+  char *mac_key_val;
+
+
+  int i;
+  int c=0;
+
+  set_logfile("stdout");
+  set_system_parameters(0);
+
+  while ((c = getopt_long(argc, argv, "hvedi:j:k:l:m:n:o:p:q:r:t:u:",long_options, &option_index)) != -1) {
+    switch(c) {
+    case 'h':
+      fprintf(stderr, "%s\n", Usage);
+      exit(1); 
+      break;
+    case 'v':
+      verbose_flag=1;
+      break;
+    case 'e':
+      encrypt_flag=1;
+      break;
+    case 'd':
+      decrypt_flag=1;
+      break;
+    case 'i':
+      //server-name
+      if ( strlen(optarg) <= OAUTH_SERVER_NAME_SIZE ) {
+        STRCPY(server_name,optarg);
+      } else {
+        fprintf(stderr,"Server-name must not exceed %d!\n", OAUTH_LTK_ID_SIZE );
+        exit(1);
+      }
+      break;
+   case 'j':
+      //long-term-key-id
+      if ( strlen(optarg) <= OAUTH_LTK_ID_SIZE ) {
+        STRCPY(kid,optarg);
+      } else {
+        fprintf(stderr,"Key ID must not exceed %d!\n", OAUTH_LTK_ID_SIZE );
+        exit(1);
+      }
+      break;
+    case 'k':
+      //long-term-key
+      if ( strlen(optarg) <= OAUTH_LTK_BASE64ENCODED_SIZE ) {
+        STRCPY(base64encoded_ltk,optarg);
+      } else {
+        fprintf(stderr,"Key must not exceed %d!\n", OAUTH_LTK_BASE64ENCODED_SIZE );
+        exit(1);
+      }
+      break;
+    case 'l':
+      //long-term-key-timestamp
+      key_timestamp = atoi(optarg);
+      break;
+    case 'm':
+      //long-term-key-lifetime
+      key_lifetime=atoi(optarg);
+      break;
+    case 'n':
+      //long-term-key-as-rs-alg
+      if ( strlen(optarg) <= OAUTH_AS_RS_ALG_SIZE ) {
+        STRCPY(as_rs_alg,optarg);
+      } else {
+        fprintf(stderr,"AS-RS Alg must not exceed %d!\n", OAUTH_AS_RS_ALG_SIZE );
+        exit(1);
+      }
+      break;
+    case 'o':
+      //token-nonce
+      nonce_val = (char*)base64_decode(optarg,strlen(optarg),&nonce_size);
+      if (nonce_size > OAUTH_GCM_NONCE_SIZE){
+        nonce_size=OAUTH_GCM_NONCE_SIZE;
+      } 
+      strncpy(gcm_nonce,nonce_val,nonce_size);
+      gcm_nonce[ nonce_size + 1 ]='\0';
+      break;
+    case 'p':
+      //token-mac-key
+      mac_key_val = (char*)base64_decode(optarg,strlen(optarg),&mac_key_size);
+      if (mac_key_size > OAUTH_MAC_KEY_SIZE){
+        mac_key_size=OAUTH_MAC_KEY_SIZE;
+      } 
+      strncpy(mac_key,mac_key_val,mac_key_size);
+      mac_key[mac_key_size+1]='\0';
+      break;
+    case 'q':
+      //token-timestamp
+      token_timestamp=strtoull(optarg,0,10);
+      break;
+    case 'r':
+      //token-lifetime
+      token_lifetime=atoi(optarg);
+      break;
+    case 't':
+      if ( strlen(optarg) <= OAUTH_TOKEN_SIZE ) {
+        STRCPY(base64encoded_etoken,optarg);
+      } else {
+        fprintf(stderr,"base64 encoded encrypted token must not exceed %d!\n", OAUTH_TOKEN_SIZE );
+        exit(1);
+      }
+      break;
+    case 'u':
+      //hmac-alg
+      if ( strlen(optarg) <= OAUTH_HMAC_ALG_SIZE ) {
+        STRCPY(hmac_alg,optarg);
+      } else {
+        fprintf(stderr,"STUN client HMAC Alg must not exceed %d!\n", OAUTH_HMAC_ALG_SIZE );
+        exit(1);
+      }
+      break;
+    default:
+      fprintf(stderr,"%s\n", Usage);
+      exit(1);
+      break;
+    }
+  }
+
+  for (i = optind; i < argc; i++)
+    printf ("Non-option argument %s\n", argv[i]);
+
+  if(optind>argc) {
+    fprintf(stderr, "%s\n", Usage);
+    exit(-1);
+  }
+
+  if (!(encrypt_flag || decrypt_flag)){
+        fprintf(stderr, "Hey, encrypt or decrypt?\n");
+         exit(-1);
+  }
+ 
+  //check if we have required params 
+  //TODO: more compact warnning handling
+  if (encrypt_flag || decrypt_flag){
+    if (strlen(server_name) == 0) { 
+        fprintf(stderr, "For encode/decode  --server-name/-i is mandatory \n");
+         exit(-1);
+    }
+    
+    if (strlen(kid) == 0){
+        fprintf(stderr, "For encode/decode  --long-term-key-id/-j is mandatory \n");
+        exit(-1);
+    }
+     if (strlen(base64encoded_ltk) == 0){
+        fprintf(stderr, "For encode/decode  --long-term-key/-k is mandatory \n");
+        exit(-1);
+    }
+    if (key_timestamp == 0){
+        fprintf(stderr, "For encode/decode  --long-term-key-timestamp/-l is mandatory \n");
+        exit(-1);
+    }
+    if (key_lifetime == 0){
+        fprintf(stderr, "For encode/decode  --long-term-key-lifetime/-m is mandatory \n");
+        exit(-1);
+    }
+
+    if (encrypt_flag && strlen(mac_key) == 0) { 
+        fprintf(stderr, "For encode --token-mac-key/-p is mandatory \n");
+        exit(-1);
+    }
+    
+    if (!encrypt_flag && decrypt_flag && strlen(base64encoded_etoken) == 0) { 
+        fprintf(stderr, "For decode --token/-t is mandatory \n");
+        exit(-1);
+    }
+
+
+    if ( setup_ikm_key(kid, base64encoded_ltk, key_timestamp, key_lifetime, as_rs_alg, &key) == 0 ) {
+          if(encrypt_flag) {
+          if (encode_token(server_name, gcm_nonce, mac_key, token_timestamp, token_lifetime, key, base64encoded_etoken) == 0 ) {
+            printf("{\n");
+            printf("    \"access_token\":\"%s\",\n",base64encoded_etoken);
+            printf("    \"token_type\":\"pop\",\n");
+            printf("    \"expires\":%d,\n",token_lifetime);
+            printf("    \"kid\":\"%s\",\n",kid);
+            printf("    \"key\":\"%s\",\n",mac_key);
+            printf("    \"alg\":\%s\n",hmac_alg);
+            printf("}\n");
+          } else {
+            fprintf(stderr, "Error during token encode\n");
+            exit(-1);
+          }
+        }
+        if (decrypt_flag) {
+          oauth_token dot;
+          if ( validate_decode_token(server_name, key, base64encoded_etoken,&dot) == 0) {
+            printf("-=Valid token!=-\n");
+              if (verbose_flag) print_token_body(&dot);
+          } else {
+            fprintf(stderr, "Error during token validation and decoding\n");
+            exit(-1);
+          }
+        }
+     } else {
+        fprintf(stderr, "Error during key setup\n");
+         exit(-1);
+    }
+
+  }
+
+  return 0;
+}

+ 15 - 0
src/apps/oauth/test.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+echo "--------------create token---------------"
+./bin/turnutils_oauth -e --server-name vvc.niif.hu --long-term-key-id 1234 --long-term-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --long-term-key-timestamp 1464077257 --long-term-key-lifetime 3600 --token-mac-key WmtzanB3ZW9peFhtdm42NzUzNG0=  --token-timestamp 92470300704768
+
+echo "---------------create and validate and print out decoded token---------------"
+./bin/turnutils_oauth -v -d -e --server-name vvc.niif.hu --long-term-key-id 1234 --long-term-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --long-term-key-timestamp 1464077257 --long-term-key-lifetime 3600 --token-mac-key WmtzanB3ZW9peFhtdm42NzUzNG0=  --token-timestamp 92470300704768
+
+echo "---------------validate token---------------"
+./bin/turnutils_oauth -d --server-name vvc.niif.hu --long-term-key-id 1234 --long-term-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --long-term-key-timestamp 1464077257 --long-term-key-lifetime 3600 --token AAwAAAAAAAAAAAAAAABbhGtRHVrDPexC4TQJppr6QNyUwpfB6fS9R3QmwjYvW6YyShKY2fbeUs5lSebE4nYQfA==
+
+echo "---------------validate and print out decoded token---------------"
+./bin/turnutils_oauth -v -d --server-name vvc.niif.hu --long-term-key-id 1234 --long-term-key SEdrajMyS0pHaXV5MDk4c2RmYXFiTmpPaWF6NzE5MjM= --long-term-key-timestamp 1464077257 --long-term-key-lifetime 3600 --token AAwAAAAAAAAAAAAAAABbhGtRHVrDPexC4TQJppr6QNyUwpfB6fS9R3QmwjYvW6YyShKY2fbeUs5lSebE4nYQfA==
+
+