A simple library to support serving https.
Dependents: oldheating gps motorhome heating
tls/tls-response.c
- Committer:
- andrewboyson
- Date:
- 2019-10-02
- Revision:
- 14:03a0b8fd6ddc
- Parent:
- 13:0a80b49a5e78
- Child:
- 15:4ddb73b5fea1
File content as of revision 14:03a0b8fd6ddc:
#include "tls-defs.h" #include "tls-connection.h" #include "tls-session.h" #include "tls-log.h" #include "tls-prf.h" #include "ser-cer.h" #include "pri-key.h" #include "log.h" #include "aes128.h" #include "random.h" #include "sha1.h" #include "tls-mac.h" #include "http.h" void backfillSize(uint8_t* pCurrent, uint8_t* pStart) { int size = pCurrent - pStart - 2; *(pStart + 0) = size >> 8; *(pStart + 1) = size & 0xFF; } void addSize(uint8_t** pp, int size) { uint8_t* p = *pp; *p++ = size >> 8; *p++ = size & 0xFF; *pp = p; } static uint8_t* pHandshakeSize; static uint8_t* pHandshakePayload; static void addHandshakeStart(uint8_t** pp) { uint8_t* p = *pp; *p++ = TLS_CONTENT_TYPE_HANDSHAKE; *p++ = 0x03; *p++ = 0x03; pHandshakeSize = p; //Store the position to backfill the handshake size p += 2; //Leave room to backfill the handshake size pHandshakePayload = p; //Record the position of the handshake payload to later calculate the hash *pp = p; } static void addHandshakeEnd(uint8_t* p, struct TlsConnection* pConnection) { backfillSize(p, pHandshakeSize); Sha256Add(&pConnection->handshakeSha, pHandshakePayload, p - pHandshakePayload); //Add the handshake hash pConnection->serverSequence++; } static void addHandshakeServerHello(uint8_t** pp, struct TlsConnection* pConnection) { Log(" sending handshake server hello\r\n"); uint8_t* p = *pp; *p++ = TLS_HANDSHAKE_SERVER_HELLO; *p++ = 0x00; uint8_t* pSize = p; p += 2; *p++ = 0x03; *p++ = 0x03; for (int i = 0; i < 32; i++) { uint8_t r = RandomGetByte(); pConnection->serverRandom[i] = r; *p++ = r; //32 bit random number } *p++ = 0x04; //SessionId length 4 *p++ = pConnection->sessionId >> 24; //Session id *p++ = pConnection->sessionId >> 16; //Session id *p++ = pConnection->sessionId >> 8; //Session id *p++ = pConnection->sessionId >> 0; //Session id *p++ = 0x00; *p++ = 0x2f; //Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) *p++ = 0x00; //Compression method none *p++ = 0x00; *p++ = 0x05; //Extensions length (2 bytes) 5 bytes *p++ = 0xff; *p++ = 0x01; //Extension Renegotiation Info *p++ = 0x00; *p++ = 0x01; //1 bytes of "Renegotiation Info" extension data follows *p++ = 0x00; //length is zero, because this is a new connection backfillSize(p, pSize); *pp = p; } static void addHandshakeCertificate(uint8_t** pp) { uint8_t* p = *pp; *p++ = TLS_HANDSHAKE_CERTIFICATE; *p++ = 0x00; addSize(&p, SerCerSize + 6); *p++ = 0x00; //Size of this handshake addSize(&p, SerCerSize + 3); *p++ = 0x00; //Size of all certificates addSize(&p, SerCerSize ); //Size of first certificate for (int i = 0; i < SerCerSize; i++) *p++ = SerCerData[i]; //Certificate *pp = p; } static void addHandshakeServerHelloDone(uint8_t** pp) { LogTime(" sending handshake server hello done\r\n"); uint8_t* p = *pp; *p++ = TLS_HANDSHAKE_SERVER_HELLO_DONE; *p++ = 0x00; addSize(&p, 0); *pp = p; } static void addHandshakeFinished(uint8_t** pp, struct TlsConnection* pConnection, struct TlsSession* pSession) { LogTime(" sending handshake finished\r\n"); uint8_t* p = *pp; //Hash over all handshake payloads exchanged so far uint8_t hash[32]; Sha256Finish(&pConnection->handshakeSha, hash); //Make verify data uint8_t verify[12]; TlsPrfServerFinished(pSession->masterSecret, hash, verify); //Hash over all handshakes //Make the 'finished' handshake uint8_t payload[16]; payload[0] = TLS_HANDSHAKE_FINISHED; payload[1] = 0x00; payload[2] = 0x00; payload[3] = 0x0c; //Length 12 for (int i = 0; i < 12; i++) payload[i + 4] = verify[i]; int payloadLength = 16; uint8_t mac[SHA1_HASH_SIZE]; TlsMacSha1(TLS_KEY_SIZE_MAC, pConnection->serverMacKey, pConnection->serverSequence, TLS_CONTENT_TYPE_HANDSHAKE, 0x03, 0x03, payloadLength, payload, mac); //plaintext uint8_t message[48]; for (int i = 0; i < 16; i++) message[i ] = payload[i]; //payload for (int i = 0; i < 20; i++) message[i + 16] = mac[i]; //mac for (int i = 0; i < 12; i++) message[i + 36] = 0x0b; //padding uint8_t iv[16]; for (int i = 0; i < 16; i++) iv[i] = RandomGetByte(); //Encrypt struct AES_ctx ctx; AES_init_ctx_iv(&ctx, pConnection->serverWriteKey, iv); AES_CBC_encrypt_buffer(&ctx, message, 48); for (int i = 0; i < 16; i++) *p++ = iv[i]; for (int i = 0; i < 48; i++) *p++ = message[i]; *pp = p; } static void addChangeCipher(uint8_t** pp, struct TlsConnection* pConnection) { LogTime(" sending change cipher\r\n"); uint8_t* p = *pp; *p++ = TLS_CONTENT_TYPE_CHANGE_CIPHER; //Content is change cipher *p++ = 0x03; *p++ = 0x03; //Legacy TLS version *p++ = 0x00; *p++ = 0x01; //Change cipher Length (2 bytes) *p++ = 0x01; //Change cipher message 1 //Record that all outgoing messages are now encrypted pConnection->serverEncrypted = true; pConnection->serverSequence = 0; *pp = p; } static void addAlert(uint8_t** pp, struct TlsConnection* pConnection, uint8_t level, uint8_t description) { LogTime(" sending alert\r\n"); Log (" - "); TlsLogAlertLevel(level); Log(": "); TlsLogAlertDescription(description); Log("\r\n"); uint8_t* p = *pp; *p++ = TLS_CONTENT_TYPE_ALERT; *p++ = 0x03; *p++ = 0x03; addSize(&p, 2); *p++ = level; *p++ = description; pConnection->serverSequence++; *pp = p; } static void sendServerHelloNew(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { uint8_t* p = pWindow; addHandshakeStart (&p); addHandshakeServerHello (&p, pConnection); addHandshakeCertificate (&p); addHandshakeServerHelloDone(&p); addHandshakeEnd ( p, pConnection); *pWindowSize = p - pWindow; pConnection->serverPositionInStreamOffset = positionOfWindowInStream + *pWindowSize; } static void sendServerHelloResume(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { uint8_t* p = pWindow; addHandshakeStart (&p); addHandshakeServerHello(&p, pConnection); addHandshakeEnd ( p, pConnection); TlsPrfKeys (pSession->masterSecret, pConnection->clientRandom, pConnection->serverRandom, pConnection->clientMacKey, pConnection->serverMacKey, pConnection->clientWriteKey, pConnection->serverWriteKey); addChangeCipher (&p, pConnection); addHandshakeStart (&p); addHandshakeFinished (&p, pConnection, pSession); addHandshakeEnd ( p, pConnection); *pWindowSize = p - pWindow; pConnection->serverPositionInStreamOffset = positionOfWindowInStream + *pWindowSize; } static void sendServerChange(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { uint8_t* p = pWindow; addChangeCipher(&p, pConnection); addHandshakeStart (&p); addHandshakeFinished(&p, pConnection, pSession); addHandshakeEnd ( p, pConnection); *pWindowSize = p - pWindow; pConnection->serverPositionInStreamOffset = positionOfWindowInStream + *pWindowSize; } static void sendFatal(uint8_t description, struct TlsConnection* pConnection, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { uint8_t* p = pWindow; addAlert(&p, pConnection, TLS_ALERT_FATAL, description); *pWindowSize = p - pWindow; pConnection->serverPositionInStreamOffset = positionOfWindowInStream + *pWindowSize; } static bool sendContent(struct TlsConnection* pConnection, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { /* content: contentType * 1 version * 2 length * 2 iv * AES_BLOCKLEN (16) message: payload * payloadLength mac * SHA1_HASH_SIZE (20) padding * 0 to AES_BLOCKLEN - 1 (0 to 15) paddingLength * 1 */ #define CONTENT_MAX_OVERHEAD (5 + AES_BLOCKLEN + SHA1_HASH_SIZE + AES_BLOCKLEN - 1 + 1) //Start LogTime(" adding application content\r\n"); LogF("- available window size %d\r\n", *pWindowSize); LogF("- position of window in stream %d\r\n", positionOfWindowInStream); uint8_t* p = pWindow; *p++ = TLS_CONTENT_TYPE_APPLICATION; *p++ = 0x03; *p++ = 0x03; //Prepare a place to backfill the size uint8_t* pBackfillSize = p; *p++ = 0; *p++ = 0; //Add the IV uint8_t* pIv = p; for (int i = 0; i < AES_BLOCKLEN; i++) *p++ = RandomGetByte(); //Add the plain payload uint8_t* pPayload = p; int payloadSize = *pWindowSize - CONTENT_MAX_OVERHEAD; LogF("- available payload size %d\r\n", payloadSize); uint32_t positionOfPayloadInStream = positionOfWindowInStream - pConnection->serverPositionInStreamOffset; LogF("- position of payload in stream %d\r\n", positionOfPayloadInStream); bool finished = HttpAdd(pConnection->id, &payloadSize, (char*)pPayload, positionOfPayloadInStream); //Return whatever HTTP would be LogF("- resulting payload size %d\r\n", payloadSize); p += payloadSize; //Add the MAC TlsMacSha1(TLS_KEY_SIZE_MAC, pConnection->serverMacKey, pConnection->serverSequence, TLS_CONTENT_TYPE_APPLICATION, 0x03, 0x03, payloadSize, pPayload, p); p += SHA1_HASH_SIZE; //Add the padding int paddingSize = AES_BLOCKLEN - 1 - (payloadSize + SHA1_HASH_SIZE + 1 - 1) % AES_BLOCKLEN; LogF("- padding size %d\r\n", paddingSize); for (int i = 0; i < paddingSize; i++) *p++ = paddingSize; //Add the padding size *p++ = paddingSize; //Backfill the size backfillSize(p, pBackfillSize); //Calculate the resulting window size *pWindowSize = p - pWindow; LogF("- resulting window size %d\r\n", *pWindowSize); //Log the plain content Log("- plain content\r\n"); LogBytesAsHex(pWindow, *pWindowSize); Log("\r\n"); //Encrypt payload + mac + padding struct AES_ctx ctx; AES_init_ctx_iv(&ctx, pConnection->serverWriteKey, pIv); AES_CBC_encrypt_buffer(&ctx, pPayload, p - pPayload); //Finalise pConnection->serverSequence++; pConnection->serverPositionInStreamOffset += *pWindowSize - payloadSize; return finished; } bool TlsResponse(int connectionId, bool clientFinished, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream) { struct TlsConnection* pConnection = TlsConnectionOrNull(connectionId); if (!pConnection) { *pWindowSize = 0; return false; } if (!pConnection->sessionId) { *pWindowSize = 0; return false; } struct TlsSession* pSession = TlsSessionOrNull(pConnection->sessionId); if (!pSession) { LogTimeF("TlsPoll - invalid session %u\r\n", pConnection->sessionId); *pWindowSize = 0; return false; } switch (pConnection->toDo) { case DO_WAIT_CLIENT_HELLO: case DO_WAIT_CLIENT_CHANGE: case DO_WAIT_DECRYPT_MASTER_SECRET: *pWindowSize = 0; if (clientFinished) return true; //The client hasn't made a request and never will so finish else return false; //The client hasn't made a request yet but it could. case DO_SEND_SERVER_HELLO_NEW: sendServerHelloNew(pConnection, pSession, pWindowSize, pWindow, positionOfWindowInStream); pConnection->toDo = DO_WAIT_CLIENT_CHANGE; return false; //Not finished case DO_SEND_SERVER_HELLO_RESUME: sendServerHelloResume(pConnection, pSession, pWindowSize, pWindow, positionOfWindowInStream); pConnection->toDo = DO_WAIT_CLIENT_CHANGE; return false; //Not finished case DO_SEND_SERVER_CHANGE: sendServerChange(pConnection, pSession, pWindowSize, pWindow, positionOfWindowInStream); pConnection->toDo = DO_APPLICATION; return false; case DO_APPLICATION: { int status = HttpPoll(connectionId, clientFinished); bool finished; switch (status) { case HTTP_WAIT: finished = false; *pWindowSize = 0; break; case HTTP_FINISHED: finished = true; *pWindowSize = 0; break; case HTTP_HAVE_SOMETHING_TO_SEND: finished = sendContent(pConnection, pWindowSize, pWindow, positionOfWindowInStream); break; } if (finished) pConnection->toDo = DO_WAIT_CLIENT_HELLO; return finished; } case DO_SEND_ALERT_ILLEGAL_PARAMETER: sendFatal(TLS_ALERT_ILLEGAL_PARAMETER, pConnection, pWindowSize, pWindow, positionOfWindowInStream); pConnection->toDo = DO_WAIT_CLIENT_HELLO; return true; //Finished case DO_SEND_ALERT_INTERNAL_ERROR: sendFatal(TLS_ALERT_INTERNAL_ERROR, pConnection, pWindowSize, pWindow, positionOfWindowInStream); pConnection->toDo = DO_WAIT_CLIENT_HELLO; return true; //Finished default: LogTimeF("TlsPoll - unspecified TLS state %d\r\n", pConnection->toDo); sendFatal(TLS_ALERT_INTERNAL_ERROR, pConnection, pWindowSize, pWindow, positionOfWindowInStream); //Internal error pConnection->toDo = DO_WAIT_CLIENT_HELLO; return true; //Finished } }