During REST authentication the user is authenticated by a third part that provides a REST token to wrtc_kit that it can use to prove to IXNX unit its user has already been authenticated.
For REST authentication to work the token creator and the token verifier (the IXNX unit) must share a shared secret that the unit in between (the wrtc_kit) does not know. wrtc_kit can pass through the token from its creator to IXNX for verification without being able to read its contents.
The instance authenticating a user shall create a REST token that prove client has been authenticated. The crytography which is used is the AEAD function (Authenticated Encryption with Associated Data) AES-256-GCM, because it allows for authentication, encryption and for the inclusion of unencrypted data in one single step. Binary parts of the result is URL-friendly base64 encoded (-_ instead of +/).
The REST authentication token contains a flexible number of data fields in JSON format that contain information given by the token creator. That information can be used by either the wrtc client or the IXNX unit – it is however only the IXNX unit that can verify the validity of the token, thus avoid using information extracted from the token before it has been validated (event onLoggedIn()|wrtc.onLoggedIn() has occurred).
Token fields are in JSON format:
You can also add your own token fields that the client can read using wrtc.adv.GetTokenVal().
To be multi platform compatible please make sure all keys and values are surrounded by quotes, separated by commas, and no spaces between.
Correct:
Incorrect:
{"e":"1657109524","u":"martinga@intertex.se","T":"2","dname":"MartinGa64","n":"mg_rauth","t":"_WFDBinqrOFdx0B95MOIkw","i":"ZePzeeWMdVvkdn5Z","c":"hx2KbwXjcUlHKmkL1TicF8RZi2hBE5b0ctd9CaGl9Sd_68gzeuPQ4ZkRgD6iOa_L-iaQMB8goz6h4XoYswnUJZw9wlV5cU3hH_PdTSZQ9olt17UWNgsUI8pOvcEaeLEzTMSInb4lnNiZ"}
mktoken is a utility to generate REST authentication tokens compatible with IXNX.
You compile mktoken using:
>gcc mktoken.c -L/usr/lib -lssl -o mktoken
mktoken.c is old and must be replaced by new code
Then you can call it like this:
>mktoken mysecret PLAIN-START n=httpname u=username e=1427896580 CRYPT-START secpar=hidden
which generates the following token:
n=httpname\nu=username\ne=1427896580\nt=rGwt1-tpDM9Q4LuliyDoBg\ni=q358cTBCZFYMCp_Z\nc=JHb04PrN0cLoxjLPSw
#include <stdio.h> #include <string.h> #include <openssl/evp.h> char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length); int IX_aes_256_gcm_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad, int aad_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext, unsigned char *tag) { EVP_CIPHER_CTX *ctx = NULL; int len; int ciphertext_len; /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) goto err_exit; /* Initialise the encryption operation. */ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) goto err_exit; /* Set IV length if default 12 bytes (96 bits) is not appropriate */ // if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) // goto err_exit; /* Initialise key and IV */ if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) goto err_exit; /* Provide any AAD data. This can be called zero or more times as * required */ if(1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) goto err_exit; /* Provide the message to be encrypted, and obtain the encrypted output. * EVP_EncryptUpdate can be called multiple times if necessary */ if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) goto err_exit; ciphertext_len = len; /* Finalise the encryption. Normally ciphertext bytes may be written at * this stage, but this does not occur in GCM mode */ if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) goto err_exit; ciphertext_len += len; /* Get the tag */ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) goto err_exit; /* Clean up */ EVP_CIPHER_CTX_free(ctx); ctx = NULL; return ciphertext_len; err_exit: if (ctx) EVP_CIPHER_CTX_free(ctx); return -1; } enum mkt_state {MKTS_PLAIN_LINE, MKTS_ENC_LINE, MKTS_INVALID}; static const char* mkt_ctrl[] = {"PLAIN-START", "CRYPT-START", NULL}; static enum mkt_state mkt_state_get(const char* ctrl) { enum mkt_state state; if (ctrl==NULL) return MKTS_INVALID; for (state = MKTS_PLAIN_LINE; state < MKTS_INVALID; state++) { if (strcmp(ctrl, mkt_ctrl[state])==0) return state; } return MKTS_INVALID; } // how to call: mktoken mysecretkey "PLAIN-START" "unencrypted-line" "unencrypted-line" ... "CRYPT-START" "encrypted-line""encrypted-line" // e.g. mktoken mysecret PLAIN-START "n=httpname" "u=username" "e=1427896580" CRYPT-START "secpar=hidden" static int ifc_do_mktoken(int argc, char **argv) { char* textu = malloc(1); //will hold unencrypted lines terminated with \n and concetenated char* texte = malloc(1); //will hold encrypted lines terminated with \n and concetenated int writeEncrypted = 0; int ctext_len = 0; static enum mkt_state mkts = MKTS_INVALID; if (!textu || !texte) { fprintf(stdout, "ERROR:mem_alloc_error1\n"); return 255; } textu[0]=0; texte[0]=0; int argidx=1; char* secret = (argidx<argc)?argv[argidx]:NULL; if (!secret) { fprintf(stdout, "ERROR:secret missing\n"); return 255; } char key[64] = {0}; strncpy(key, secret, 32); argidx++; char* control = (argidx<argc)?argv[argidx]:NULL; if ((mkts=mkt_state_get(control))==MKTS_INVALID) { fprintf(stdout, "ERROR:control (expected as argv[%d]) not found\n", argidx); return 255; } while (++argidx<argc) { const char* arg = argv[argidx]; //check first for control tag enum mkt_state mkt_state_new = mkt_state_get(arg); if (mkt_state_new != MKTS_INVALID) { mkts = mkt_state_new; if (mkts==MKTS_PLAIN_LINE) writeEncrypted=0; else if (mkts==MKTS_ENC_LINE) writeEncrypted=1; continue; } switch (mkts) { case MKTS_PLAIN_LINE: case MKTS_ENC_LINE: { char* buf = writeEncrypted?texte:textu; char* newbuf = realloc(buf, strlen(buf) + strlen(arg)+2); //+2 = /n + term zero if (!newbuf) { fprintf(stdout, "ERROR:mem_alloc_error3\n"); return 255; } sprintf(newbuf+strlen(newbuf), "%s\n", arg); if (writeEncrypted) texte=newbuf; else textu=newbuf; } break; default: fprintf(stdout, "ERROR:invalid_parsing_state\n"); return 255; } //switch end } //while end (argument parsing) int texte_len = strlen(texte); if (texte[texte_len-1]=='\n') texte_len--; //remove \n at the end of encrpted block char* ctext = (char*)malloc(texte_len+64);//+64 for sanity if (!ctext) { fprintf(stdout, "ERROR:mem_alloc_error4\n"); return 255; } char tag[16]; char iv[12] = {0}; srand(time(NULL)); int i; for (i=0;i<sizeof(iv);i++) iv[i] = rand() & 0xff; ctext_len = IX_aes_256_gcm_encrypt((unsigned char*)texte, texte_len, (unsigned char*)textu, strlen(textu), (unsigned char*)key, (unsigned char*)iv, (unsigned char*)ctext, (unsigned char*)tag); if (ctext_len!=texte_len) { fprintf(stdout, "ERROR:aes_256_gcm_encrypt_failed\n"); return 255; } size_t ctext_b64_len = 0; char *ctext_b64 = base64_encode((unsigned char*)ctext, ctext_len, &ctext_b64_len); size_t iv_b64_len = 0; char* iv_b64 = base64_encode((unsigned char*)iv,sizeof(iv),&iv_b64_len); size_t tag_b64_len = 0; char* tag_b64 = base64_encode((unsigned char*)tag,sizeof(tag),&tag_b64_len); if (iv_b64 && tag_b64) { char* newbuf = NULL; /* first append the tag (==hash) to the unencrypted buffer */ newbuf = realloc(textu, strlen(textu) + 2 + strlen(tag_b64)+2); //+2 = "e=", +2 = /n + term zero if (newbuf) { textu = newbuf; sprintf(textu+strlen(textu), "t=%s\n", tag_b64); } else { fprintf(stdout, "ERROR:mem_alloc_error7\n"); return 255; } /* second append the initial vector to the unencrypted buffer */ newbuf = realloc(textu, strlen(textu) + 2 + strlen(iv_b64)+2); //+2 = "e=", +2 = /n + term zero if (newbuf) { textu = newbuf; sprintf(textu+strlen(textu), "i=%s\n", iv_b64); } else { fprintf(stdout, "ERROR:mem_alloc_error6\n"); return 255; } /* last, append the encrypted data to the unencrypted buffer */ if (ctext_b64 && strlen(ctext_b64)) { newbuf = realloc(textu, strlen(textu) + 2 + strlen(ctext_b64)+2); //+2 = "e=", +2 = /n + term zero if (newbuf) { textu = newbuf; sprintf(textu+strlen(textu), "c=%s\n", ctext_b64); } else { fprintf(stdout, "ERROR:mem_alloc_error5\n"); return 255; } } } else { fprintf(stdout, "ERROR:mem_alloc_error8\n"); return 255; } fprintf(stdout, "%s", textu); return 0; } int main (int argc, char *argv[]) { if (0==ifc_do_mktoken(argc, argv)) return 0; else return -1; } /* In case you do not have base64_encode() available here is some code that you can use for this: */ static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'};//- and _ instead of + and / --> URL-friendly ! static char *decoding_table = NULL; static size_t mod_table[] = {0, 2, 1}; char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) { *output_length = 4 * ((input_length + 2) / 3); char *encoded_data = (char*)malloc(*output_length+1); if (encoded_data == NULL) return NULL; size_t i,j; for (i = 0, j = 0; i < input_length;) { unsigned int octet_a = i < input_length ? data[i++] : 0; unsigned int octet_b = i < input_length ? data[i++] : 0; unsigned int octet_c = i < input_length ? data[i++] : 0; unsigned int triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = 0; encoded_data[*output_length]=0; return encoded_data; }