From 0e5a990d3530b83429f6459287f1a11a7633e08d Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sat, 17 Feb 2018 15:24:41 +0000 Subject: [PATCH] Add functionality to encrypt tickets exchanged between endpoints The communication between the SSO server and the /sso_login endpoint of the target service (the 't' query argument) can be encrypted using the NaCl cryptobox, with a new set of keypairs for the SSO service and the target services. --- src/mod_sso/mod_sso.c | 146 ++++++++++++++++++++------ src/mod_sso/mod_sso.h | 5 + src/mod_sso/sso_utils.c | 150 ++++++++++++++++----------- src/sso/Makefile.am | 1 + src/sso/sso_encrypt.c | 124 ++++++++++++++++++++++ src/sso/sso_encrypt.h | 30 ++++++ src/sso/test/Makefile.am | 3 + src/sso/test/sso_encrypt_unittest.cc | 63 +++++++++++ 8 files changed, 431 insertions(+), 91 deletions(-) create mode 100644 src/sso/sso_encrypt.c create mode 100644 src/sso/sso_encrypt.h create mode 100644 src/sso/test/sso_encrypt_unittest.cc diff --git a/src/mod_sso/mod_sso.c b/src/mod_sso/mod_sso.c index 20c5b7b..d2a3490 100644 --- a/src/mod_sso/mod_sso.c +++ b/src/mod_sso/mod_sso.c @@ -61,6 +61,12 @@ typedef struct { // Note: public_key is a binary buffer (non zero-terminated). const unsigned char *public_key; + // Encryption keys are fixed-size binary buffers too. + const unsigned char *encryption_public_key; + const unsigned char *local_encryption_public_key; + char *local_encryption_public_key_base64; + const unsigned char *local_encryption_secret_key; + // Same for the session_key (not fixed size though, so we store the // size as well). const unsigned char *session_key; @@ -70,6 +76,10 @@ typedef struct { apr_array_header_t *groups; } modsso_config; +static int modsso_config_ticket_encryption_enabled(modsso_config *s_cfg) { + return (s_cfg->encryption_public_key && s_cfg->local_encryption_public_key && s_cfg->local_encryption_secret_key); +} + typedef const char *(*CMD_HAND_TYPE)(); static char *groups_array_to_commasep_string(apr_pool_t *p, @@ -105,6 +115,10 @@ static void *create_modsso_config(apr_pool_t *p, char *s) { newcfg->service = NULL; newcfg->domain = NULL; newcfg->public_key = NULL; + newcfg->encryption_public_key = NULL; + newcfg->local_encryption_public_key = NULL; + newcfg->local_encryption_public_key_base64 = NULL; + newcfg->local_encryption_secret_key = NULL; newcfg->session_key = NULL; newcfg->session_key_len = 0; newcfg->groups = NULL; @@ -122,10 +136,16 @@ static void *merge_modsso_config(apr_pool_t *p, void *base, void *add) { cadd->login_server ? cadd->login_server : cbase->login_server; newcfg->service = cadd->service ? cadd->service : cbase->service; newcfg->domain = cadd->domain ? cadd->domain : cbase->domain; - newcfg->public_key = cbase->public_key; - if (cadd->public_key) { - newcfg->public_key = cadd->public_key; + newcfg->public_key = cadd->public_key ? cadd->public_key : cbase->public_key; + newcfg->encryption_public_key = cadd->encryption_public_key ? cadd->encryption_public_key : cbase->encryption_public_key; + newcfg->local_encryption_secret_key = cadd->local_encryption_secret_key ? cadd->local_encryption_secret_key : cbase->local_encryption_secret_key; + newcfg->local_encryption_public_key = cbase->local_encryption_public_key; + newcfg->local_encryption_public_key_base64 = cbase->local_encryption_public_key_base64; + if (cadd->local_encryption_public_key) { + newcfg->local_encryption_public_key = cadd->local_encryption_public_key; + newcfg->local_encryption_public_key_base64 = cadd->local_encryption_public_key_base64; } + newcfg->session_key = cbase->session_key; newcfg->session_key_len = cbase->session_key_len; if (cadd->session_key) { @@ -163,46 +183,59 @@ static const char *set_modsso_domain(cmd_parms *parms, void *mconfig, static const char *set_modsso_public_key_file(cmd_parms *parms, void *mconfig, const char *arg) { modsso_config *s_cfg = (modsso_config *)mconfig; - char buf[128]; - apr_size_t n = sizeof(buf); - apr_file_t *file; - int status; - if (apr_file_open(&file, arg, APR_FOPEN_READ, 0, parms->pool) != - APR_SUCCESS) { - return "Could not open SSOPublicKeyFile"; + if (modsso_read_fixed_size_file(parms->pool, arg, SSO_PUBLIC_KEY_SIZE, &s_cfg->public_key) < 0) { + return "Could not read SSOPublicKeyFile"; } - status = apr_file_read(file, (void *)buf, &n); - apr_file_close(file); - if (status != APR_SUCCESS) { - return "Could not read contents of SSOPublicKeyFile"; + return NULL; +} + +static const char *set_modsso_session_key_file(cmd_parms *parms, void *mconfig, + const char *arg) { + modsso_config *s_cfg = (modsso_config *)mconfig; + + if (modsso_read_fixed_size_file(parms->pool, arg, MODSSO_SESSION_KEY_SIZE, &s_cfg->session_key) < 0) { + return "Could not read SSOSessionKeyFile"; } + s_cfg->session_key_len = MODSSO_SESSION_KEY_SIZE; + return NULL; +} - unsigned char *key = (unsigned char *)apr_palloc(parms->pool, n); - memcpy(key, buf, n); - s_cfg->public_key = key; +static const char *set_modsso_encryption_public_key_file(cmd_parms *parms, void *mconfig, const char *arg) { + modsso_config *s_cfg = (modsso_config *)mconfig; + if (modsso_read_fixed_size_file(parms->pool, arg, SSO_TICKET_ENCRYPTION_PUBLIC_KEY_SIZE, &s_cfg->encryption_public_key) < 0) { + return "Could not read SSOEncryptionPublicKeyFile"; + } return NULL; } -static const char *set_modsso_session_key_file(cmd_parms *parms, void *mconfig, - const char *arg) { +static const char *set_modsso_local_encryption_public_key_file(cmd_parms *parms, void *mconfig, const char *arg) { modsso_config *s_cfg = (modsso_config *)mconfig; - unsigned char *session_key = NULL; - size_t session_key_len = MODSSO_SESSION_KEY_SIZE; + size_t sz; - session_key = (unsigned char *)apr_palloc(parms->pool, session_key_len); - if (modsso_session_read_key_from_file(parms->pool, arg, session_key, - &session_key_len) < 0) { - return "Could not open SSOSessionKeyFile"; + if (modsso_read_fixed_size_file(parms->pool, arg, SSO_TICKET_ENCRYPTION_PUBLIC_KEY_SIZE, &s_cfg->local_encryption_public_key) < 0) { + return "Could not read SSOLocalEncryptionPublicKeyFile"; } - s_cfg->session_key = session_key; - s_cfg->session_key_len = session_key_len; + sz = SSO_TICKET_ENCRYPTION_PUBLIC_KEY_SIZE * 2; + s_cfg->local_encryption_public_key_base64 = apr_palloc(parms->pool, sz); + if (sso_serialize_ticket_encryption_public_key(s_cfg->local_encryption_public_key, s_cfg->local_encryption_public_key_base64, sz) < 0) { + return "Failure to base64-encode the SSOLocalEncryptionPublicKeyFile"; + } return NULL; } +static const char *set_modsso_local_encryption_secret_key_file(cmd_parms *parms, void *mconfig, const char *arg) { + modsso_config *s_cfg = (modsso_config *)mconfig; + + if (modsso_read_fixed_size_file(parms->pool, arg, SSO_TICKET_ENCRYPTION_SECRET_KEY_SIZE, &s_cfg->local_encryption_secret_key) < 0) { + return "Could not read SSOEncryptionSecretKeyFile"; + } + return NULL; +} + static const command_rec mod_sso_cmds[] = { AP_INIT_TAKE1("SSOLoginServer", (CMD_HAND_TYPE)set_modsso_login_server, NULL, OR_ALL, @@ -215,6 +248,18 @@ static const command_rec mod_sso_cmds[] = { "SSOPublicKeyFile", (CMD_HAND_TYPE)set_modsso_public_key_file, NULL, RSRC_CONF, "SSOPublicKeyFile (string) Location of the login server public key"), + AP_INIT_TAKE1( + "SSOEncryptionPublicKeyFile", (CMD_HAND_TYPE)set_modsso_encryption_public_key_file, NULL, + RSRC_CONF, + "SSOEncryptionPublicKeyFile (string) Location of the login server SSO ticket encryption public key"), + AP_INIT_TAKE1( + "SSOLocalEncryptionPublicKeyFile", (CMD_HAND_TYPE)set_modsso_local_encryption_public_key_file, NULL, + RSRC_CONF, + "SSOLocalEncryptionPublicKeyFile (string) Location of the local SSO ticket encryption public key"), + AP_INIT_TAKE1( + "SSOLocalEncryptionSecretKeyFile", (CMD_HAND_TYPE)set_modsso_local_encryption_secret_key_file, NULL, + RSRC_CONF, + "SSOLocalEncryptionSecretKeyFile (string) Location of the local SSO ticket encryption secret key"), AP_INIT_TAKE1( "SSOSessionKeyFile", (CMD_HAND_TYPE)set_modsso_session_key_file, NULL, RSRC_CONF, @@ -385,9 +430,49 @@ static int check_config(request_rec *r, modsso_config *s_cfg) { "sso: SSOPublicKeyFile is not defined!"); return 0; } + if ((s_cfg->encryption_public_key || s_cfg->local_encryption_public_key || s_cfg->local_encryption_secret_key) + && !(s_cfg->encryption_public_key && s_cfg->local_encryption_public_key && s_cfg->local_encryption_secret_key)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "sso: all of SSOEncryptionPublicKey, SSOLocalEncryptionPublicKey and SSOLocalEncryptionSecretKey must be defined to enable ticket encryption!"); + } return 1; } +static int modsso_get_params_from_request(modsso_config *s_cfg, request_rec *r, struct modsso_params *params) { + if (modsso_parse_query_string(r->pool, r->args, params) < 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "sso: get_params_from_request: invalid query parameters"); + return -1; + } + + // If we have a local encryption key + if (modsso_config_ticket_encryption_enabled(s_cfg)) { + char ticket_buf[512]; + + if (!params->et || !params->et[0]) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "sso: request is missing the 'et' query parameter"); + return -1; + } + + if (sso_ticket_decrypt(params->et, s_cfg->encryption_public_key, s_cfg->local_encryption_secret_key, ticket_buf, sizeof(ticket_buf)) < 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "sso: could not decrypt ticket from request"); + return -1; + } + + params->t = apr_pstrdup(r->pool, ticket_buf); + } else { + if (!params->t || !params->t[0]) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "sso: request is missing the 't' query parameter"); + return -1; + } + } + + return 0; +} + /** * Apache method handler for mod_sso. * @@ -452,8 +537,8 @@ static int mod_sso_method_handler(request_rec *r) { return HTTP_BAD_REQUEST; } - // Parse query params - if (modsso_parse_query_string(r->pool, r->args, ¶ms) < 0) { + // Parse query params, including decrypting the ticket if necessary. + if (modsso_get_params_from_request(s_cfg, r, ¶ms) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "sso: invalid parameters for sso_login: %s", r->args); return HTTP_BAD_REQUEST; @@ -530,6 +615,9 @@ static int redirect_to_login_server(request_rec *r, modsso_config *s_cfg, unique_id, sso_login_path); } } + if (modsso_config_ticket_encryption_enabled(s_cfg)) { + login_url = apr_pstrcat(r->pool, login_url, "&k=", s_cfg->local_encryption_public_key_base64, NULL); + } if (groups) { login_url = apr_pstrcat(r->pool, login_url, diff --git a/src/mod_sso/mod_sso.h b/src/mod_sso/mod_sso.h index 5bad103..b3f6dd6 100644 --- a/src/mod_sso/mod_sso.h +++ b/src/mod_sso/mod_sso.h @@ -25,6 +25,7 @@ #include "ap_config.h" #include "apr_strings.h" #include <sso/sso.h> +#include <sso/sso_encrypt.h> #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(sso); @@ -44,6 +45,7 @@ struct request_rec; struct modsso_params { char *t; + char *et; char *d; }; @@ -63,6 +65,9 @@ void modsso_set_cookie(request_rec *r, const char *cookie_name, void modsso_del_cookie(request_rec *r, const char *cookie_name); +int modsso_read_fixed_size_file(apr_pool_t *pool, const char *path, + size_t size, const unsigned char **out); + // session.c int modsso_session_read_key_from_file(apr_pool_t *pool, const char *path, unsigned char *out, size_t *outsz); diff --git a/src/mod_sso/sso_utils.c b/src/mod_sso/sso_utils.c index 0977d45..5f9f49c 100644 --- a/src/mod_sso/sso_utils.c +++ b/src/mod_sso/sso_utils.c @@ -22,16 +22,18 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <ctype.h> + +#include "ap_config.h" +#include "apr_strings.h" #include "httpd.h" + #include "http_config.h" #include "http_core.h" #include "http_log.h" -#include "apr_strings.h" -#include "http_protocol.h" #include "http_main.h" -#include "ap_config.h" +#include "http_protocol.h" #include "mod_sso.h" -#include <ctype.h> // Ugly implementation of url decoding. Returns a newly allocated // string (using malloc). @@ -40,48 +42,48 @@ char *modsso_url_decode(apr_pool_t *p, const char *i) { char state = 'N'; char buf[3], obuf[2]; for (o = output; *i; i++) { - switch(state) { + switch (state) { case 'N': - if (*i == '%') state = 1; - else *o++ = *i; + if (*i == '%') + state = 1; + else + *o++ = *i; + break; + case 1: { + char c = tolower(*i); + buf[0] = *i; + if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) { + state = 2; + } else { + *o++ = '%'; + *o++ = *i; + state = 'N'; + } + break; + } + case 2: { + char c = tolower(*i); + if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) { + buf[1] = *i; + buf[2] = 0; + obuf[0] = strtol(buf, 0, 16); + obuf[1] = 0; + if (obuf[0]) + *o++ = obuf[0]; + } else { + *o++ = '%'; + *o++ = buf[0]; + *o++ = *i; + } + state = 'N'; break; - case 1: - { - char c = tolower(*i); - buf[0] = *i; - if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) { - state = 2; - } else { - *o++ = '%'; - *o++ = *i; - state = 'N'; - } - break; - } - case 2: - { - char c = tolower(*i); - if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) { - buf[1] = *i; - buf[2] = 0; - obuf[0] = strtol(buf, 0, 16); - obuf[1] = 0; - if (obuf[0]) - *o++ = obuf[0]; - } else { - *o++ = '%'; - *o++ = buf[0]; - *o++ = *i; - } - state = 'N'; - break; - } + } } } if (state != 'N') { *o++ = buf[0]; if (state > 1) - *o++ = buf[1]; + *o++ = buf[1]; } *o = '\0'; return output; @@ -105,11 +107,13 @@ char *modsso_url_encode(apr_pool_t *p, const char *s) { return out; } -int modsso_parse_query_string(apr_pool_t *p, const char *str, modsso_params_t params) { +int modsso_parse_query_string(apr_pool_t *p, const char *str, + modsso_params_t params) { char *tmp = apr_pstrdup(p, str), *strptr = NULL; params->d = NULL; params->t = NULL; + params->et = NULL; if (str == NULL) { return 0; @@ -136,31 +140,30 @@ int modsso_parse_query_string(apr_pool_t *p, const char *str, modsso_params_t pa params->d = value; } else if (!strcmp(token, "t")) { params->t = value; + } else if (!strcmp(token, "et")) { + params->et = value; } } - if (!params->d || !params->d[0] - || !params->t || !params->t[0]) { + // Both 'd' and one of 't' or 'et' must be set. + if (!params->d || !params->d[0] || + ((!params->t || !params->t[0]) && (!params->et || !params->et[0]))) { return -1; } return 0; } -static char *make_cookie_value(request_rec *r, - const char *name, - const char *session_id, - const char *path, - int cookie_lifespan) -{ +static char *make_cookie_value(request_rec *r, const char *name, + const char *session_id, const char *path, + int cookie_lifespan) { size_t sz = 1024; char *cookie_value = (char *)apr_palloc(r->pool, sz); if (cookie_lifespan == 0) { - - snprintf(cookie_value, sz, - "%s=%s; path=%s; secure; httpOnly;", - name, session_id, path); + + snprintf(cookie_value, sz, "%s=%s; path=%s; secure; httpOnly;", name, + session_id, path); } else { char expires[200]; time_t t; @@ -168,8 +171,7 @@ static char *make_cookie_value(request_rec *r, t = time(NULL) + cookie_lifespan; tmp = gmtime(&t); strftime(expires, sizeof(expires), "%a, %d-%b-%Y %H:%M:%S GMT", tmp); - snprintf(cookie_value, sz, - "%s=%s; expires=%s; path=%s; secure; httpOnly;", + snprintf(cookie_value, sz, "%s=%s; expires=%s; path=%s; secure; httpOnly;", name, session_id, expires, path); } return cookie_value; @@ -185,7 +187,7 @@ static char *make_cookie_value(request_rec *r, char *modsso_get_cookie(request_rec *r, const char *cookie_name) { char *rv = NULL, *cookies, *cookie, *tokenizerCtx = NULL; int cookie_name_len = strlen(cookie_name); - + const char *cookies_c = apr_table_get(r->headers_in, "Cookie"); if (cookies_c == NULL) { return NULL; @@ -198,7 +200,8 @@ char *modsso_get_cookie(request_rec *r, const char *cookie_name) { while (cookie) { while (*cookie == ' ') cookie++; - if (strncmp(cookie, cookie_name, cookie_name_len) == 0 && cookie[cookie_name_len] == '=') { + if (strncmp(cookie, cookie_name, cookie_name_len) == 0 && + cookie[cookie_name_len] == '=') { cookie += (cookie_name_len + 1); rv = apr_pstrdup(r->pool, cookie); break; @@ -208,20 +211,43 @@ char *modsso_get_cookie(request_rec *r, const char *cookie_name) { return rv; } -void modsso_set_cookie(request_rec *r, const char *cookie_name, - const char *value, const char *path) -{ +void modsso_set_cookie(request_rec *r, const char *cookie_name, + const char *value, const char *path) { char *cookie_value; cookie_value = make_cookie_value(r, cookie_name, value, path, 0); - apr_table_setn(r->err_headers_out, "Set-Cookie", + apr_table_setn(r->err_headers_out, "Set-Cookie", apr_pstrdup(r->pool, cookie_value)); } -void modsso_del_cookie(request_rec *r, const char *cookie_name) -{ +void modsso_del_cookie(request_rec *r, const char *cookie_name) { char cookie_value[512]; sprintf(cookie_value, "%s=; expires=Mar, 01-01-1971 00:00:00 GMT", cookie_name); apr_table_setn(r->err_headers_out, "Set-Cookie", apr_pstrdup(r->pool, cookie_value)); } + +int modsso_read_fixed_size_file(apr_pool_t *pool, const char *path, size_t size, + const unsigned char **out) { + char *m = NULL; + int status; + apr_file_t *file; + apr_size_t n; + + if (apr_file_open(&file, path, APR_FOPEN_READ, 0, pool) != APR_SUCCESS) + goto fail; + + n = size; + m = apr_palloc(pool, n); + status = apr_file_read(file, m, &n); + apr_file_close(file); + if (status != APR_SUCCESS || n != size) + goto fail; + + *out = (unsigned char *)m; + return 0; + +fail: + // apr_pfree(pool, m); + return -1; +} diff --git a/src/sso/Makefile.am b/src/sso/Makefile.am index deb9bcf..56b1646 100644 --- a/src/sso/Makefile.am +++ b/src/sso/Makefile.am @@ -8,6 +8,7 @@ ssotool_LDADD = libsso.la libsso_la_SOURCES = \ sso.c sso.h \ + sso_encrypt.c sso_encrypt.h \ base64.c \ randombytes.c \ tweetnacl.c tweetnacl.h diff --git a/src/sso/sso_encrypt.c b/src/sso/sso_encrypt.c new file mode 100644 index 0000000..df1e8c8 --- /dev/null +++ b/src/sso/sso_encrypt.c @@ -0,0 +1,124 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "sso.h" +#include "tweetnacl.h" + +extern void randombytes(unsigned char *, unsigned long long); + +int sso_generate_encryption_keys(unsigned char *publicp, + unsigned char *secretp) { + return crypto_box_keypair(publicp, secretp); +} + +int sso_serialize_ticket_encryption_public_key(const unsigned char *public_key, + char *out, size_t out_sz) { + size_t sz = out_sz; + return sso_base64_encode((unsigned char *)out, &sz, public_key, + crypto_box_PUBLICKEYBYTES); +} + +int sso_ticket_encrypt(const char *serialized_ticket, + const char *serialized_pub_key, + const unsigned char *secret_key, char *out, + size_t out_sz) { + unsigned char pub_key[crypto_box_PUBLICKEYBYTES]; + unsigned char n[crypto_box_NONCEBYTES]; + unsigned char *m = NULL, *c = NULL; + int serialized_ticket_len; + int mlen; + int ret = -1; + size_t sz, enc_sz, nonce_sz, pub_sz; + + // Decode the public key (base64-encoded). + pub_sz = crypto_box_PUBLICKEYBYTES; + ret = sso_base64_decode(pub_key, &pub_sz, (unsigned char *)serialized_pub_key, + strlen(serialized_pub_key)); + if (ret < 0) + return ret; + + // Encrypt the serialized ticket string. + serialized_ticket_len = strlen(serialized_ticket); + mlen = serialized_ticket_len + crypto_box_ZEROBYTES; + c = malloc(mlen); + m = malloc(mlen); + memset(m, 0, crypto_box_ZEROBYTES); + memcpy(m + crypto_box_ZEROBYTES, serialized_ticket, serialized_ticket_len); + randombytes(n, crypto_box_NONCEBYTES); + + crypto_box(c, m, mlen, n, pub_key, secret_key); + + // Build the output string by concatenating the nonce and the + // encrypted message, each base64-encoded, separated by a dot. + nonce_sz = out_sz; + ret = sso_base64_encode((unsigned char *)out, &nonce_sz, n, + crypto_box_NONCEBYTES); + if (ret < 0) + goto fail; + + out[nonce_sz++] = '.'; + sz = out_sz - nonce_sz; + enc_sz = mlen - crypto_box_BOXZEROBYTES; + ret = sso_base64_encode((unsigned char *)(out + nonce_sz), &sz, + c + crypto_box_BOXZEROBYTES, enc_sz); + +fail: + free(m); + free(c); + return ret; +} + +int sso_ticket_decrypt(const char *encrypted, const unsigned char *public_key, + const unsigned char *secret_key, char *out, + size_t out_sz) { + const char *nonce, *enc_msg; + unsigned char n[crypto_box_NONCEBYTES]; + unsigned char *c = NULL, *m = NULL; + int nonce_b64len, enc_msg_b64len, clen; + size_t nonce_sz, enc_msg_sz; + int ret; + + nonce = encrypted; + enc_msg = strchr(encrypted, '.'); + if (!enc_msg) + return -1; + nonce_b64len = enc_msg - nonce; + enc_msg++; + enc_msg_b64len = strlen(enc_msg); + + nonce_sz = crypto_box_NONCEBYTES; + ret = sso_base64_decode(n, &nonce_sz, (unsigned char *)nonce, nonce_b64len); + if (ret < 0) + goto fail; + if (nonce_sz != crypto_box_NONCEBYTES) { + ret = -1; + goto fail; + } + + c = malloc(crypto_box_BOXZEROBYTES + enc_msg_b64len); + memset(c, 0, crypto_box_BOXZEROBYTES); + enc_msg_sz = enc_msg_b64len; + ret = sso_base64_decode(c + crypto_box_BOXZEROBYTES, &enc_msg_sz, + (unsigned char *)enc_msg, enc_msg_b64len); + if (ret < 0) + goto fail; + + clen = crypto_box_BOXZEROBYTES + enc_msg_sz; + m = malloc(clen); + + ret = crypto_box_open(m, c, clen, n, public_key, secret_key); + if (ret < 0) + goto fail; + + memcpy(out, m + crypto_box_ZEROBYTES, clen - crypto_box_ZEROBYTES); + out[clen - crypto_box_ZEROBYTES] = '\0'; + +fail: + if (c) + free(c); + if (m) + free(m); + return ret; +} diff --git a/src/sso/sso_encrypt.h b/src/sso/sso_encrypt.h new file mode 100644 index 0000000..d5c6e2c --- /dev/null +++ b/src/sso/sso_encrypt.h @@ -0,0 +1,30 @@ +#ifndef __sso_encrypt_H +#define __sso_encrypt_H 1 + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Must match tweetnacl.h */ +#define SSO_TICKET_ENCRYPTION_PUBLIC_KEY_SIZE 32 +#define SSO_TICKET_ENCRYPTION_SECRET_KEY_SIZE 32 + +int sso_generate_encryption_keys(unsigned char *publicp, + unsigned char *secretp); +int sso_serialize_ticket_encryption_public_key(const unsigned char *public_key, + char *out, size_t out_sz); +int sso_ticket_encrypt(const char *serialized_ticket, + const char *serialized_pub_key, + const unsigned char *secret_key, char *out, + size_t out_sz); +int sso_ticket_decrypt(const char *encrypted, const unsigned char *public_key, + const unsigned char *secret_key, char *out, + size_t out_sz); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sso/test/Makefile.am b/src/sso/test/Makefile.am index 56096ad..65f7cf6 100644 --- a/src/sso/test/Makefile.am +++ b/src/sso/test/Makefile.am @@ -2,6 +2,7 @@ include $(top_srcdir)/Makefile.defs check_PROGRAMS = \ sso_unittest \ + sso_encrypt_unittest \ tweetnacl_unittest TESTS = $(check_PROGRAMS) @@ -11,4 +12,6 @@ LDADD = $(GTEST_LIBS) ../libsso.la sso_unittest_SOURCES = sso_unittest.cc +sso_encrypt_unittest_SOURCES = sso_encrypt_unittest.cc + tweetnacl_unittest_SOURCES = tweetnacl_unittest.cc diff --git a/src/sso/test/sso_encrypt_unittest.cc b/src/sso/test/sso_encrypt_unittest.cc new file mode 100644 index 0000000..14101db --- /dev/null +++ b/src/sso/test/sso_encrypt_unittest.cc @@ -0,0 +1,63 @@ +#include <gtest/gtest.h> +#include <stdlib.h> +#include <string.h> + +#include "sso.h" +#include "sso_encrypt.h" + +extern "C" { +#include "tweetnacl.h" +} + +using std::string; + +namespace { + +class Encryption : public testing::Test { +protected: + virtual void SetUp() { sso_generate_encryption_keys(public_key, secret_key); } + + unsigned char public_key[crypto_box_PUBLICKEYBYTES]; + unsigned char secret_key[crypto_box_SECRETKEYBYTES]; +}; + +TEST_F(Encryption, EncryptAndDecrypt) { + const char *serialized_ticket = "hello world I am a ticket"; + char serialized_pubkey[64]; + char buf[512]; + char out[512]; + + ASSERT_EQ(0, sso_serialize_ticket_encryption_public_key( + public_key, serialized_pubkey, sizeof(serialized_pubkey))); + printf("serialized public key: %s (%lu bytes)\n", serialized_pubkey, + strlen(serialized_pubkey)); + ASSERT_EQ(0, sso_ticket_encrypt(serialized_ticket, serialized_pubkey, + secret_key, buf, sizeof(buf))); + printf("encrypted ticket: %s\n", buf); + ASSERT_EQ(0, + sso_ticket_decrypt(buf, public_key, secret_key, out, sizeof(out))); + + ASSERT_EQ(string(out), string(serialized_ticket)); +} + +TEST_F(Encryption, DecryptFailsWithBadData) { + const char *not_valid[] = { + "abcdefgh", "", "something.else", "dGVzdDE=.dGVzdDI=", NULL, + }; + char out[1024]; + int i; + + for (i = 0; not_valid[i]; i++) { + ASSERT_LT(sso_ticket_decrypt(not_valid[i], public_key, secret_key, out, + sizeof(out)), + 0) + << "no error decrypting: " << not_valid[i]; + } +} + +} // namespace + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- GitLab