A simple library to support serving https.

Dependents:   oldheating gps motorhome heating

Revision:
14:03a0b8fd6ddc
Parent:
13:0a80b49a5e78
Child:
15:4ddb73b5fea1
--- a/tls/tls-response.c	Fri Sep 27 11:31:18 2019 +0000
+++ b/tls/tls-response.c	Wed Oct 02 20:26:04 2019 +0000
@@ -12,90 +12,96 @@
 #include "tls-mac.h"
 #include "http.h"
 
-void addSize(uint8_t** ppCurrent, int size)
-{
-    uint8_t* p = *ppCurrent;
-    *p++ = size >> 8;
-    *p++ = size & 0xFF;
-    *ppCurrent = p;
-}
 void backfillSize(uint8_t* pCurrent, uint8_t* pStart)
 {
     int size = pCurrent - pStart - 2;
     *(pStart + 0) = size >> 8;
     *(pStart + 1) = size & 0xFF;
 }
-static void sendServerHello(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream)
+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)
 {
-    //Set up a multiple handshakes content - hello, certificate and done
-    LogTime("     sending server hello\r\n");
-    uint8_t* p = pWindow;
-    *p++ = TLS_CONTENT_TYPE_HANDSHAKE;                 //Content is handshakes
-    *p++ = 0x03; *p++ = 0x03;                          //Legacy TLS version
-    uint8_t* pHandshakesLength = p;
-    p += 2;                                            //Handshakes Length (2 bytes)
-    uint8_t* handshakesPayloadStart = p;               //Record the start of the handshake payload     
+    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
     
-    //Server hello handshake
-    *p++ = TLS_HANDSHAKE_SERVER_HELLO;                 //Handshake type server hello
+    *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* pHandshakeHelloLength = p;
-    p += 2;                                            //Size of this handshake
-    *p++ = 0x03; *p++ = 0x03;                          //TLS version 1.2
+    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++ = 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, pHandshakeHelloLength);
+    *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);
     
-    //Certificate handshake
-    *p++ = TLS_HANDSHAKE_CERTIFICATE; *p++ = 0x00;             //Handshake type certificate
+    *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
     
-    //Hello done handshake
-    *p++ = TLS_HANDSHAKE_SERVER_HELLO_DONE; *p++ = 0x00;       //Handshake type server hello done
-    *p++ = 0; *p++ = 0;                                        //Size of this handshake
-    backfillSize(p, pHandshakesLength);
+    *pp = p;
+}
+static void addHandshakeServerHelloDone(uint8_t** pp)
+{   
+    LogTime("     sending handshake server hello done\r\n");
+    uint8_t* p = *pp;
     
-    //Finalise the handshake content
-    int handshakeLength = p - handshakesPayloadStart;
-    Sha256Add(&pConnection->handshakeSha, handshakesPayloadStart, handshakeLength); //Add the handshake hash
-    *pWindowSize = p - pWindow;
+    *p++ = TLS_HANDSHAKE_SERVER_HELLO_DONE; *p++ = 0x00;
+    addSize(&p, 0);
+    
+    *pp = p;
 }
-static void sendServerChange(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream)
+static void addHandshakeFinished(uint8_t** pp, struct TlsConnection* pConnection, struct TlsSession* pSession)
 {
-    LogTime("     sending server change\r\n");
-    uint8_t* p = pWindow;
-    *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 incoming messages are now encrypted
-    pConnection->serverEncrypted = true;
-    pConnection->serverSequence = 0;
-    
-    LogTime("     sending server handshake finished\r\n");
-    *p++ = TLS_CONTENT_TYPE_HANDSHAKE;                             //Content is handshakes
-    *p++ = 0x03; *p++ = 0x03;                                      //Legacy TLS version
-    uint8_t* pHandshakesLength = p;
-    p += 2;                                                        //Handshakes Length (2 bytes)
+    LogTime("     sending handshake finished\r\n");
+    uint8_t* p = *pp;
     
     //Hash over all handshake payloads exchanged so far
     uint8_t hash[32];
@@ -141,28 +147,99 @@
     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];
+    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
     
-    //Finalise
-    backfillSize(p, pHandshakesLength);
+    //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 sendFatal(char description, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream)
+static void sendServerHelloResume(struct TlsConnection* pConnection, struct TlsSession* pSession, int* pWindowSize, uint8_t* pWindow, uint32_t positionOfWindowInStream)
 {
-    LogTime("     sending fatal alert: ");
-    TlsLogAlertDescription(description);
-    Log("\r\n");
     uint8_t* p = pWindow;
-    *p++ = TLS_CONTENT_TYPE_ALERT;                               //Content is alert
-    *p++ = 0x03; *p++ = 0x03;                                    //Legacy TLS version
-    addSize(&p, 2);                                              //Alert Length (2 bytes)
+    
+    addHandshakeStart      (&p);
+    addHandshakeServerHello(&p, pConnection);
+    addHandshakeEnd        ( p, pConnection);
     
-    *p++ = 2;                                                    //Fatal (level = 2)
-    *p++ = description;                                          //Description
+    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)
@@ -280,8 +357,13 @@
             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:
-            sendServerHello(pConnection, pSession, pWindowSize, pWindow, positionOfWindowInStream);
+         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
             
@@ -304,18 +386,18 @@
             return finished;
         }   
         case DO_SEND_ALERT_ILLEGAL_PARAMETER:
-            sendFatal(TLS_ALERT_ILLEGAL_PARAMETER, pWindowSize, pWindow, positionOfWindowInStream);
+            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, pWindowSize, pWindow, positionOfWindowInStream);
+            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, pWindowSize, pWindow, positionOfWindowInStream); //Internal error
+            sendFatal(TLS_ALERT_INTERNAL_ERROR, pConnection, pWindowSize, pWindow, positionOfWindowInStream); //Internal error
             pConnection->toDo = DO_WAIT_CLIENT_HELLO;
             return true; //Finished
     }