#include #include #include #include #include #include #include "rserv.h" #include "crypto.h" #include "flow.h" #include "slitheen.h" /** Updates the hash of all TLS handshake messages upon the * receipt of a new message. This hash is eventually used * to verify the TLS Finished message * * Inputs: * f: the tagged flow * hs: A pointer to the start of the handshake message * * Output: * 0 on success, 1 on failure */ int update_finish_hash(flow *f, uint8_t *hs){ //find handshake length const struct handshake_header *hs_hdr; uint8_t *p = hs; hs_hdr = (struct handshake_header*) p; uint32_t hs_len = HANDSHAKE_MESSAGE_LEN(hs_hdr); EVP_DigestUpdate(f->finish_md_ctx, hs, hs_len+4); return 0; } /** Extracts the server parameters from the server key * exchange message * * Inputs: * f: the tagged flow * hs: the beginning of the server key exchange * handshake message * * Output: * 0 on success, 1 on failure */ int extract_parameters(flow *f, uint8_t *hs){ DH *dh; uint8_t *p; long i; p = hs + HANDSHAKE_HEADER_LEN; if((dh = DH_new()) == NULL){ return 1; } /* Extract prime modulus */ n2s(p,i); if(!(dh->p = BN_bin2bn(p,i,NULL))){ return 1; } p += i; /* Extract generator */ n2s(p,i); if(!(dh->g = BN_bin2bn(p,i,NULL))){ return 1; } p += i; /* Extract server public value */ n2s(p,i); if(!(dh->pub_key = BN_bin2bn(p,i,NULL))){ return 1; } f->dh = dh; return 0; } /** MAC a message * TODO: look at tls1_mac in t1_enc.c * For now, only goes one way (macs message to be written) * int32_t mac(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incoming, int32_t type, int32_t enc){ uint8_t header[13]; int32_t md_size; header[8] = type; header[9] = 0x03;//TODO: update for different versions header[10] = 0x03; header[11] = (len) >> 8; header[12] = (len) & 0xff; EVP_DigestSignUpdate(f->read_mac_ctx, header, sizeof(header)); EVP_DigestSignUpdate(f->read_mac_ctx, input, len); EVP_DigestSignFinal(f->read_mac_ctx, output, &md_size); return md_size; }*/ /* Encrypt/Decrypt a TLS record * * Inputs: * f: the tagged flow * input: a pointer to the data that is to be encrypted/ * decrypted * output: a pointer to where the data should be written * after it is encrypted or decrypted * len: the length of the data * incoming: the direction of the record * type: the type of the TLS record * enc: 1 for encryption, 0 for decryption * * Output: * length of the output data */ int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incoming, int32_t type, int32_t enc){ uint8_t *p = input; EVP_CIPHER_CTX *ds = (incoming) ? ((enc) ? f->srvr_write_ctx : f->clnt_read_ctx) : ((enc) ? f->clnt_write_ctx : f->srvr_read_ctx) ; if(ds == NULL){ printf("FAIL\n"); return 1; } //TODO: wrap my mind around this, might need 2 more uint8_t *seq; seq = (incoming) ? f->read_seq : f->write_seq; if(f->application && (ds->iv[EVP_GCM_TLS_FIXED_IV_LEN] == 0)){ //fill in rest of iv for(int i = EVP_GCM_TLS_FIXED_IV_LEN; i< ds->cipher->iv_len; i++){ ds->iv[i] = p[i- EVP_GCM_TLS_FIXED_IV_LEN]; } } #ifdef DEBUG printf("\t\tiv: "); for(int i=0; icipher->iv_len; i++){ printf("%02X ", ds->iv[i]); } printf("\n"); #endif uint8_t buf[13]; memcpy(buf, seq, 8); for(int i=7; i>=0; i--){ ++seq[i]; if(seq[i] != 0) break; } buf[8] = type; buf[9] = 0x03; buf[10] = 0x03; buf[11] = len >> 8; //len >> 8; buf[12] = len & 0xff;//len *0xff; int32_t pad = EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_AEAD_TLS1_AAD, 13, buf); // = int32_t pad? if(enc) len += pad; int32_t n = EVP_Cipher(ds, p, p, len); //decrypt in place if(n<0) return 0; if(!enc) p[EVP_GCM_TLS_EXPLICIT_IV_LEN+n] = '\0'; return n; } /** Verifies the hash in a TLS finished message * * Inputs: * f: the tagged flow * p: a pointer to the TLS Finished handshake message * incoming: the direction of the flow * * Output: * 0 on success, 1 on failure */ int verify_finish_hash(flow *f, uint8_t *p, int32_t incoming){ EVP_MD_CTX ctx; uint8_t hash[EVP_MAX_MD_SIZE]; uint32_t hash_len; EVP_MD_CTX_init(&ctx); //get header length struct handshake_header *hs_hdr; hs_hdr = (struct handshake_header*) p; uint32_t fin_length = HANDSHAKE_MESSAGE_LEN(hs_hdr); p += HANDSHAKE_HEADER_LEN; //finalize hash of handshake msgs EVP_MD_CTX_copy_ex(&ctx, f->finish_md_ctx); EVP_DigestFinal_ex(&ctx, hash, &hash_len); //now use pseudorandom function uint8_t *output = calloc(1, fin_length); if(incoming){ PRF(f->master_secret, SSL3_MASTER_SECRET_SIZE, (uint8_t *) TLS_MD_SERVER_FINISH_CONST, TLS_MD_SERVER_FINISH_CONST_SIZE , hash, hash_len, NULL, 0, NULL, 0, output, fin_length); } else { PRF(f->master_secret, SSL3_MASTER_SECRET_SIZE, (uint8_t *) TLS_MD_CLIENT_FINISH_CONST, TLS_MD_CLIENT_FINISH_CONST_SIZE , hash, hash_len, NULL, 0, NULL, 0, output, fin_length); } //now compare if(CRYPTO_memcmp(p, output, fin_length) != 0){ // printf("VERIFY FAILED\n"); return 1; } else { printf("VERIFY PASSED\n"); } return 0; } /** Computes the TLS master secret from the decoy server's * public key parameters and the leaked secret from the * extracted Slitheen tag * * Input: * f: the tagged flow * * Output: * 0 on success, 1 on failure */ int compute_master_secret(flow *f){ DH *dh_srvr = NULL; DH *dh_clnt = NULL; BN_CTX *ctx; BN_MONT_CTX *mont = NULL; BIGNUM *pub_key = NULL, *priv_key = NULL; ctx = BN_CTX_new(); dh_srvr = f->dh; dh_clnt = DHparams_dup(dh_srvr); uint32_t l = dh_clnt->length ? dh_clnt->length : BN_num_bits(dh_clnt->p) - 1; int32_t bytes = (l+7) / 8; uint8_t *buf = (uint8_t *)OPENSSL_malloc(bytes); if (buf == NULL){ BNerr(BN_F_BNRAND, ERR_R_MALLOC_FAILURE); return 1; } pub_key = BN_new(); priv_key = BN_new(); PRF(f->key, 16, (uint8_t *) SLITHEEN_KEYGEN_CONST, SLITHEEN_KEYGEN_CONST_SIZE, NULL, 0, NULL, 0, NULL, 0, buf, bytes); #ifdef DEBUG printf("Generated the following rand bytes: "); for(int i=0; i< bytes; i++){ printf(" %02x ", buf[i]); } printf("\n"); #endif if (!BN_bin2bn(buf, bytes, priv_key)) return 1; { BIGNUM *prk; prk = priv_key; if (!dh_clnt->meth->bn_mod_exp(dh_clnt, pub_key, dh_clnt->g, prk, dh_clnt->p, ctx, mont)){ printf("FAIL\n"); return 1; } } dh_clnt->pub_key = pub_key; dh_clnt->priv_key = priv_key; // Compute master key uint8_t *pre_master_secret = calloc(1, 256);//TODO: find right length DH_compute_key(pre_master_secret, dh_srvr->pub_key, dh_clnt); PRF(pre_master_secret, PRE_MASTER_LEN, (uint8_t *) TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, f->client_random, SSL3_RANDOM_SIZE, f->server_random, SSL3_RANDOM_SIZE, NULL, 0, f->master_secret, SSL3_MASTER_SECRET_SIZE); if(f->current_session != NULL) memcpy(f->current_session, f->master_secret, SSL3_MASTER_SECRET_SIZE); #ifdef DEBUG fprintf(stderr, "Premaster Secret:\n"); BIO_dump_fp(stderr, (char *)pre_master_secret, PRE_MASTER_LEN); fprintf(stderr, "Client Random:\n"); BIO_dump_fp(stderr, (char *)f->client_random, SSL3_RANDOM_SIZE); fprintf(stderr, "Server Random:\n"); BIO_dump_fp(stderr, (char *)f->server_random, SSL3_RANDOM_SIZE); fprintf(stderr, "Master Secret:\n"); BIO_dump_fp(stderr, (char *)f->master_secret, SSL3_MASTER_SECRET_SIZE); #endif //remove pre_master_secret from memory memset(pre_master_secret, 0, PRE_MASTER_LEN); free(pre_master_secret); DH_free(dh_srvr); DH_free(dh_clnt); return 0; } /** Saves the random none from the server hello message * * Inputs: * f: the tagged flow * hs: a pointer to the beginning of the server hello msg * * Output: * none */ void extract_server_random(flow *f, uint8_t *hs){ uint8_t *p; p = hs + HANDSHAKE_HEADER_LEN; p+=2; //skip version memcpy(f->server_random, p, SSL3_RANDOM_SIZE); } /** PRF using sha384, as defined in RFC 5246 * * Inputs: * secret: the master secret used to sign the hash * secret_len: the length of the master secret * seed{1, ..., 4}: seed values that are virtually * concatenated * seed{1,...4}_len: length of the seeds * output: a pointer to the output of the PRF * output_len: the number of desired bytes * * Output: * 0 on success, 1 on failure */ int PRF(uint8_t *secret, int32_t secret_len, uint8_t *seed1, int32_t seed1_len, uint8_t *seed2, int32_t seed2_len, uint8_t *seed3, int32_t seed3_len, uint8_t *seed4, int32_t seed4_len, uint8_t *output, int32_t output_len){ EVP_MD_CTX ctx, ctx_tmp, ctx_init; EVP_PKEY *mac_key; const EVP_MD *md = EVP_sha384(); uint8_t A[EVP_MAX_MD_SIZE]; size_t len, A_len; int chunk = EVP_MD_size(md); int remaining = output_len; uint8_t *out = output; EVP_MD_CTX_init(&ctx); EVP_MD_CTX_init(&ctx_tmp); EVP_MD_CTX_init(&ctx_init); EVP_MD_CTX_set_flags(&ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, secret, secret_len); /* Calculate first A value */ EVP_DigestSignInit(&ctx_init, NULL, md, NULL, mac_key); EVP_MD_CTX_copy_ex(&ctx, &ctx_init); if(seed1 != NULL && seed1_len > 0){ EVP_DigestSignUpdate(&ctx, seed1, seed1_len); } if(seed2 != NULL && seed2_len > 0){ EVP_DigestSignUpdate(&ctx, seed2, seed2_len); } if(seed3 != NULL && seed3_len > 0){ EVP_DigestSignUpdate(&ctx, seed3, seed3_len); } if(seed4 != NULL && seed4_len > 0){ EVP_DigestSignUpdate(&ctx, seed4, seed4_len); } EVP_DigestSignFinal(&ctx, A, &A_len); //iterate until desired length is achieved while(remaining > 0){ /* Now compute SHA384(secret, A+seed) */ EVP_MD_CTX_copy_ex(&ctx, &ctx_init); EVP_DigestSignUpdate(&ctx, A, A_len); EVP_MD_CTX_copy_ex(&ctx_tmp, &ctx); if(seed1 != NULL && seed1_len > 0){ EVP_DigestSignUpdate(&ctx, seed1, seed1_len); } if(seed2 != NULL && seed2_len > 0){ EVP_DigestSignUpdate(&ctx, seed2, seed2_len); } if(seed3 != NULL && seed3_len > 0){ EVP_DigestSignUpdate(&ctx, seed3, seed3_len); } if(seed4 != NULL && seed4_len > 0){ EVP_DigestSignUpdate(&ctx, seed4, seed4_len); } if(remaining > chunk){ EVP_DigestSignFinal(&ctx, out, &len); out += len; remaining -= len; /* Next A value */ EVP_DigestSignFinal(&ctx_tmp, A, &A_len); } else { EVP_DigestSignFinal(&ctx, A, &A_len); memcpy(out, A, remaining); remaining -= remaining; } } return 0; } /** After receiving change cipher spec, calculate keys from master secret * * Input: * f: the tagged flow * * Output: * 0 on success, 1 on failure */ int init_ciphers(flow *f){ EVP_CIPHER_CTX *r_ctx; EVP_CIPHER_CTX *w_ctx; EVP_CIPHER_CTX *w_ctx_srvr; EVP_CIPHER_CTX *r_ctx_srvr; const EVP_CIPHER *c = EVP_aes_256_gcm(); /* Generate Keys */ uint8_t *write_key, *write_iv; uint8_t *read_key, *read_iv; int32_t mac_len, key_len, iv_len; key_len = EVP_CIPHER_key_length(c); iv_len = EVP_CIPHER_iv_length(c); //EVP_GCM_TLS_FIXED_IV_LEN; mac_len = EVP_MD_size(EVP_sha384()); int32_t total_len = key_len + iv_len + mac_len; total_len *= 2; uint8_t *key_block = calloc(1, total_len); PRF(f->master_secret, SSL3_MASTER_SECRET_SIZE, (uint8_t *) TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE, f->server_random, SSL3_RANDOM_SIZE, f->client_random, SSL3_RANDOM_SIZE, NULL, 0, key_block, total_len); #ifdef DEBUG printf("master secret:\n"); for(int i=0; i< SSL3_MASTER_SECRET_SIZE; i++){ printf("%02x ", f->master_secret[i]); } printf("\n"); printf("client random:\n"); for(int i=0; i< SSL3_RANDOM_SIZE; i++){ printf("%02x ", f->client_random[i]); } printf("\n"); printf("server random:\n"); for(int i=0; i< SSL3_RANDOM_SIZE; i++){ printf("%02x ", f->server_random[i]); } printf("\n"); printf("keyblock:\n"); for(int i=0; i< total_len; i++){ printf("%02x ", key_block[i]); } printf("\n"); #endif iv_len = EVP_GCM_TLS_FIXED_IV_LEN; write_key = key_block; read_key = key_block + key_len; write_iv = key_block + 2*key_len; read_iv = key_block + 2*key_len + iv_len; /* Initialize Cipher Contexts */ r_ctx = EVP_CIPHER_CTX_new(); w_ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(r_ctx); EVP_CIPHER_CTX_init(w_ctx); w_ctx_srvr = EVP_CIPHER_CTX_new(); r_ctx_srvr = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(w_ctx_srvr); EVP_CIPHER_CTX_init(r_ctx_srvr); /* Initialize MACs --- not needed for aes_256_gcm write_mac = key_block + 2*key_len + 2*iv_len; read_mac = key_block + 2*key_len + 2*iv_len + mac_len; read_mac_ctx = EVP_MD_CTX_create(); write_mac_ctx = EVP_MD_CTX_create(); read_mac_key =EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, read_mac, mac_len); write_mac_key =EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, write_mac, mac_len); EVP_DigestSignInit(read_mac_ctx, NULL, EVP_sha384(), NULL, read_mac_key); EVP_DigestSignInit(write_mac_ctx, NULL, EVP_sha384(), NULL, write_mac_key); EVP_PKEY_free(read_mac_key); EVP_PKEY_free(write_mac_key);*/ #ifdef DEBUG { int i; fprintf(stderr, "EVP_CipherInit_ex(r_ctx,c,key=,iv=,which)\n"); fprintf(stderr, "\tkey= "); for (i = 0; i < c->key_len; i++) fprintf(stderr, "%02x", read_key[i]); fprintf(stderr, "\n"); fprintf(stderr, "\t iv= "); for (i = 0; i < c->iv_len; i++) fprintf(stderr, "%02x", read_iv[i]); fprintf(stderr, "\n"); } { int i; fprintf(stderr, "EVP_CipherInit_ex(w_ctx,c,key=,iv=,which)\n"); fprintf(stderr, "\tkey= "); for (i = 0; i < c->key_len; i++) fprintf(stderr, "%02x", write_key[i]); fprintf(stderr, "\n"); fprintf(stderr, "\t iv= "); for (i = 0; i < c->iv_len; i++) fprintf(stderr, "%02x", write_iv[i]); fprintf(stderr, "\n"); } #endif EVP_CipherInit_ex(r_ctx, c, NULL, read_key, NULL, 0); EVP_CipherInit_ex(w_ctx, c, NULL, write_key, NULL, 1); EVP_CipherInit_ex(w_ctx_srvr, c, NULL, read_key, NULL, 1); EVP_CipherInit_ex(r_ctx_srvr, c, NULL, write_key, NULL, 0); EVP_CIPHER_CTX_ctrl(r_ctx, EVP_CTRL_GCM_SET_IV_FIXED, EVP_GCM_TLS_FIXED_IV_LEN, read_iv); EVP_CIPHER_CTX_ctrl(w_ctx, EVP_CTRL_GCM_SET_IV_FIXED, EVP_GCM_TLS_FIXED_IV_LEN, write_iv); EVP_CIPHER_CTX_ctrl(w_ctx_srvr, EVP_CTRL_GCM_SET_IV_FIXED, EVP_GCM_TLS_FIXED_IV_LEN, read_iv); EVP_CIPHER_CTX_ctrl(r_ctx_srvr, EVP_CTRL_GCM_SET_IV_FIXED, EVP_GCM_TLS_FIXED_IV_LEN, write_iv); f->clnt_read_ctx = r_ctx; f->clnt_write_ctx = w_ctx; f->srvr_read_ctx = r_ctx_srvr; f->srvr_write_ctx = w_ctx_srvr; return 0; } // To avoid warnings about MAC paddings, use this to update contexts void update_context(flow *f, uint8_t *input, int32_t len, int32_t incoming, int32_t type, int32_t enc){ uint8_t *output = calloc(1, len+16+8); memcpy(output + EVP_GCM_TLS_EXPLICIT_IV_LEN, input, len); //If the original message was a decryption, this will be an necryption. //Incoming field stays the same encrypt(f, output, output, len+8, incoming, type, !enc); //revert the sequence number uint8_t *seq = incoming ? f->read_seq : f->write_seq; for(int i=7; i>=0; i--){ --seq[i]; if(seq[i] >= 0) break; else seq[i] = 0; } free(output); } /** Checks a handshake message to see if it is tagged or a * recognized flow. If the client random nonce is tagged, * adds the flow to the flow table to be tracked. * * Inputs: * info: the processed packet * f: the tagged flow * * Output: * none */ void check_handshake(struct packet_info *info, flow f){ FILE *fp; int res, i, code; uint8_t *hello_rand; const struct handshake_header *handshake_hdr; byte privkey[PTWIST_BYTES]; byte key[16]; uint8_t *p = info->app_data + RECORD_HEADER_LEN; handshake_hdr = (struct handshake_header*) p; code = handshake_hdr->type; if (code == 0x01){ p += CLIENT_HELLO_HEADER_LEN; //now pointing to hello random :D hello_rand = p; p += 4; //skipping time bytes /* Load the private key */ fp = fopen("privkey", "rb"); if (fp == NULL) { perror("fopen"); exit(1); } res = fread(privkey, PTWIST_BYTES, 1, fp); if (res < 1) { perror("fread"); exit(1); } fclose(fp); /* check tag*/ res = check_tag(key, privkey, p, (const byte *)"context", 7); if (res) { printf("Untagged\n"); } else { fp = fopen("tags", "ab"); if (fp == NULL) { perror("fopen"); exit(1); } //Write tag to file for(i=0; i< 28; i++){ fprintf(fp, "%02x ", p[i]); } fprintf(fp, "\n"); fclose(fp); //Write key to file fp = fopen("sharedkey", "ab"); if (fp == NULL) { perror("fopen"); exit(1); } for(i=0; i<16;i++){ fprintf(fp, "%02x", key[i]); } fprintf(fp, "\n"); fclose(fp); printf("Received tagged flow! (key ="); for(i=0; i<16;i++){ printf(" %02x", key[i]); } printf(")\n"); /* Save flow in table */ flow *flow_ptr = add_flow(f); for(int i=0; i<16; i++){ flow_ptr->key[i] = key[i]; } memcpy(flow_ptr->client_random, hello_rand, SSL3_RANDOM_SIZE); printf("Saved new flow\n"); } } }