====== REST token generator ======
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 +/).
===== Token fields =====
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 [[ixnx:wrtc:js:api:wrtc:onloggedin|onLoggedIn()|wrtc.onLoggedIn()]] has occurred).
Token fields are in JSON format:
* "t":"//token//" - the token hashsum that validates the token
* "i":"//initial vector//" - hashsum nonce
* "n":"//name//" - login name that was used when acquiring the token
* "u":"//name//" - username to set wrtc.myname to
* "a":"//name//" - SIP authentication name
* "P":"//password//" - SIP authentication password (preferably included into c field below)
* "s":"//start//" - valid from time of token
* "e":"//expire//" - expiration time of token
* "p":"//path//" - token refresh path to set wrtc.rpath to
* "telnr":"//number//" - telephone number (alternative SIP addr.)
* "dname":"//name//" - SIP display name
* "T":"//number(s)//" - template numbers allowed to be used. Several numbers can be specified, separated by spaces.
* "c":"//data//" - encrypted data only readable by IXNX unit. May contain any of the above fields.
* Other token fields may be added in the future. All fields except "t": are optional.
You can also add your own token fields that the client can read using [[ixnx:wrtc:js:api:wrtc:adv.gettokenval|wrtc.adv.GetTokenVal()]].
===== Use correct format =====
To be multi platform compatible please make sure //all// keys and values are surrounded by quotes, separated by commas, and //no spaces between//.
Correct:
* "e":"1657109524","u":"martinga@intertex.se",
Incorrect:
* e:"1657109524",u:"martinga@intertex.se",
* e:1657109524,u:"martinga@intertex.se",
* "e":"1657109524", "u":"martinga@intertex.se",
* e:1657109524, u:"martinga@intertex.se",
* "e":1657109524, "u":"martinga@intertex.se",
===== Example =====
{"e":"1657109524","u":"martinga@intertex.se","T":"2","dname":"MartinGa64","n":"mg_rauth","t":"_WFDBinqrOFdx0B95MOIkw","i":"ZePzeeWMdVvkdn5Z","c":"hx2KbwXjcUlHKmkL1TicF8RZi2hBE5b0ctd9CaGl9Sd_68gzeuPQ4ZkRgD6iOa_L-iaQMB8goz6h4XoYswnUJZw9wlV5cU3hH_PdTSZQ9olt17UWNgsUI8pOvcEaeLEzTMSInb4lnNiZ"}
===== mktoken =====
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
#include
#include
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 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;
}