/** * @brief Initiate an XSSL connection with server. * * @param xssl * * @return 1 on success, <1 on failure */ int XSSL_connect(XSSL *xssl) { char buf[XIA_MAXBUF]; int n; /* Send CLIENT HELLO */ sprintf(buf, "CLIENT HELLO"); if ((n = Xsend(xssl->sockfd, buf, strlen(buf), 0)) != strlen(buf)) { ERROR("ERROR sending CLIENT HELLO"); return 0; } /* Wait for SERVER HELLO */ // Receive public key + temporary public key signed by server's long-term private key // Continue receiving until we see "SERVER DONE" int offset = 0; while ( offset < 11 || strcmp("SERVER DONE", &buf[offset-11]) != 0 ) { n = Xrecv(xssl->sockfd, &buf[offset], sizeof(buf)-offset, 0); if (n < 0) { ERROR("ERROR receiving SERVER HELLO"); return n; } if (n == 0) { ERROR("ERROR: server closed connection during SERVER HELLO"); return n; } offset += n; } /* Parse public keys from SERVER HELLO */ offset = strlen("SERVER HELLO"); char *keys = &buf[offset]; // start of signed portion of message uint32_t *keybufsizeptr = (uint32_t*)&buf[offset]; uint32_t keybufsize = *keybufsizeptr; // TODO: error checking here offset += sizeof(uint32_t); RSA *pubkey = deserialize_rsa_pub_key(&buf[offset], keybufsize); offset += keybufsize; uint32_t *tempkeybufsizeptr = (uint32_t*)&buf[offset]; uint32_t tempkeybufsize = *tempkeybufsizeptr; offset += sizeof(uint32_t); RSA *sessionPubkey = deserialize_rsa_pub_key(&buf[offset], tempkeybufsize); offset += tempkeybufsize; uint32_t *siglenptr = (uint32_t*)&buf[offset]; uint32_t siglen = *siglenptr; offset += sizeof(uint32_t); char* sig = &buf[offset]; offset += siglen; DBGF("Received keys:\n\tkeylen: %d\n\ttempkeylen: %d\n\tsiglen: %d", keybufsize, tempkeybufsize, siglen); /* Verify two things: * 1) hash(key) == SID so we trust the signature * 2) verify the sig so we trust the temp key */ sockaddr_x *sa = (sockaddr_x*)malloc(sizeof(sockaddr_x)); socklen_t len = sizeof(sockaddr_x); if (Xgetpeername(xssl->sockfd, (struct sockaddr*)sa, &len) < 0) { ERRORF("Error in Xgetpeername on socket %d: %s", xssl->sockfd, strerror(errno)); return 0; } Graph g(sa); Node sid_node = g.get_final_intent(); DBGF("SID: %s", sid_node.to_string().c_str()); char *sid_from_key_hash = SID_from_keypair(pubkey); DBGF("Pub key hash: %s", sid_from_key_hash); if (strcmp(sid_node.to_string().c_str(), sid_from_key_hash) != 0) { WARN("Hash of received public key does not match SID! Closing connection."); return 0; } if (verify(pubkey, keys, keybufsize+tempkeybufsize+2*sizeof(uint32_t), sig, siglen) != 1) { WARN("Unable to verify signature on temporary RSA keypair! Closing connection."); return 0; } /* Generate pre-master secret and send to server, encrypted with sessionPubKey */ unsigned char* pms = (unsigned char*)malloc(PRE_MASTER_SECRET_LENGTH); if (RAND_bytes(pms, PRE_MASTER_SECRET_LENGTH) != 1) { ERROR("ERROR: Couldn't generate pre-master secret"); return 0; } if (VERBOSITY >= V_DEBUG) { DBG("Pre-Master Secret:"); print_bytes(pms, PRE_MASTER_SECRET_LENGTH); } int ciphertext_len; if ( (ciphertext_len = pub_encrypt(sessionPubkey, pms, PRE_MASTER_SECRET_LENGTH, buf, XIA_MAXBUF)) == -1 ) { ERROR("ERROR: Unable to encrypt session key"); return 0; } n = 0; offset = 0; while (offset < ciphertext_len) { if ((n = Xsend(xssl->sockfd, &buf[offset], ciphertext_len-offset, 0)) < 0) { ERROR("ERROR sending pre-master secret"); return 0; } offset += n; } /* Init symmetric session ciphers with pre-master secret. Client encrypt context initialized with same key data as server decrypt context and vice versa. */ uint32_t salt[] = SALT; if (aes_init(pms, PRE_MASTER_SECRET_LENGTH/2, &pms[PRE_MASTER_SECRET_LENGTH/2], PRE_MASTER_SECRET_LENGTH/2, (unsigned char *)&salt, xssl->en, xssl->de)) { ERROR("ERROR initializing AES ciphers"); return 0; } free(pms); /* For now, omitting CLIENT DONE */ return 1; }
int EncryptionModule::handshake_in(char *buf, size_t *datalen, size_t *buflen) { (void)buflen; /* pick up where we left off in the handshake */ switch(handshake_status_) { case kWaitForClientHello: // server-side { /* make sure the data is a complete client hello */ if (strncmp("CLIENT HELLO", buf, 12) != 0) { ERROR("ERROR: received message was not CLIENT HELLO\n"); return ERR_NEED_MORE_DATA; } else { // FIXME: inefficient! // int hello_len = strlen("CLIENT HELLO"); // char* newbuf = (char*)malloc(*buflen); // memcpy(newbuf, buf+hello_len, *datalen-hello_len); // *datalen -= hello_len; // free(buf); // buf = newbuf; // FIXME: need pointer to pointer handshake_status_ = kSendServerHello; return 0; } } case kWaitForServerHello: // client-side { /* Wait for SERVER HELLO */ // Receive public key + temporary public key signed by server's long-term private key // Continue receiving until we see "SERVER DONE" // FIXME: this assumes there's no handshake data after us! if (*datalen < 11 || strcmp("SERVER DONE", (buf+*datalen-11)) != 0) { ERROR("Did not receive complete SERVER DONE. Waiting for more data\n"); return ERR_NEED_MORE_DATA; } /* Parse public keys from SERVER HELLO */ int offset = strlen("SERVER HELLO"); char *keys = buf+offset; // start of signed portion of message uint32_t *keybufsizeptr = (uint32_t*)(buf+offset); uint32_t keybufsize = *keybufsizeptr; // TODO: error checking here offset += sizeof(uint32_t); RSA *pubkey = deserialize_rsa_pub_key(buf+offset, keybufsize); offset += keybufsize; uint32_t *tempkeybufsizeptr = (uint32_t*)(buf+offset); uint32_t tempkeybufsize = *tempkeybufsizeptr; offset += sizeof(uint32_t); session_pub_key_ = deserialize_rsa_pub_key(buf+offset, tempkeybufsize); offset += tempkeybufsize; uint32_t *siglenptr = (uint32_t*)(buf+offset); uint32_t siglen = *siglenptr; offset += sizeof(uint32_t); char* sig = buf+offset; offset += siglen; DBGF("Received keys:\n\tkeylen: %d\n\ttempkeylen: %d\n\tsiglen: %d", keybufsize, tempkeybufsize, siglen); /* Verify two things: * 1) in the real world, verify the key in the cert * 2) verify the sig so we trust the temp key */ if (verify(pubkey, keys, keybufsize+tempkeybufsize+2*sizeof(uint32_t), sig, siglen) != 1) { WARN("Unable to verify signature on temporary RSA keypair! Closing connection."); return ERR_GENERIC; } // FIXME: inefficient! // char* newbuf = (char*)malloc(*buflen); // int used_len = offset + strlen("SERVER DONE"); // memcpy(newbuf, buf+used_len, *datalen-used_len); // *datalen -= used_len; // free(buf); // buf = newbuf; // FIXME need pointer to pointer handshake_status_ = kSendPreMasterSecret; return 0; } case kWaitForPreMasterSecret: // server-side { /* Receive and decrypt pre-master secret */ size_t expecting = RSA_size(session_keypair_); // TODO: have client send key size? if (*datalen < expecting) { WARN("Did not receive full PMS. Waiting for more data.\n"); return ERR_NEED_MORE_DATA; } unsigned char* pms = (unsigned char*)malloc(RSA_size(session_keypair_)); int plaintext_len; if ( (plaintext_len = priv_decrypt(session_keypair_, pms, RSA_size(session_keypair_), (unsigned char*)buf, *datalen)) == -1) { // FIXME what if there's more data for modules above us? ERROR("ERROR decrypting session key"); return ERR_GENERIC; } if (plaintext_len != PRE_MASTER_SECRET_LENGTH) { ERROR("Decrypted key material is not of size PRE_MASTER_SECRET_LENGTH"); return ERR_GENERIC; } if (VERBOSITY >= V_DEBUG) { DBG("Pre_Master Secret:"); //print_bytes(pms, PRE_MASTER_SECRET_LENGTH); } /* Init symmetric session ciphers with pre-master secret. Client encrypt context initialized with same key data as server decrypt context and vice versa. */ uint32_t salt[] = SALT; if (aes_init(&pms[PRE_MASTER_SECRET_LENGTH/2], PRE_MASTER_SECRET_LENGTH/2, pms, PRE_MASTER_SECRET_LENGTH/2, (unsigned char *)&salt, en_, de_)) { ERROR("ERROR initializing AES ciphers"); return ERR_GENERIC; } free(pms); /* remove our handshake data before next module gets buf */ // FIXME: inefficient! // char* newbuf = (char*)malloc(*buflen); // memcpy(newbuf, buf+expecting, *datalen-expecting); // *datalen -= expecting; // free(buf); // buf = newbuf; // FIXME need pointer to pointer /* done with handshake */ handshake_status_ = kDone; handshake_done_ = true; ready_ = true; return 0; } default: ERROR("Unknown handshake status\n"); //printf("handshake status: %d\n", handshake_status_); return ERR_GENERIC; } }