Search code examples
cwebsocketsha1libtomcrypt

Computing websocket Sec-WebSocket-Accept value using libtomcrypt


RFC6455 specifies a method of computing the Sec-WebSocket-Accept response header from the value of the Sec-WebSocket-Key header. This method is based on SHA-1 hashing and Base64-encoding the result.

How can I implement this method in plain C using libtomcrypt for SHA-1 and Base64?

Note: This question intentionally does not show any effort because I immediately answered it myself. See below for my effort.


Solution

  • Here's a full compilable example that uses only libtomcrypt without any dynamic memory allocation and successfully computes the reference example from RFC6455:

    //This file is licensed under CC0 1.0 Universal (public domain)
    //Compile like this: gcc -o wsencodetest wsencodetest.c -ltomcrypt
    #include <stdio.h>
    #include <string.h>
    #include <stdint.h>
    #include <ctype.h>
    #include <tomcrypt.h>
    
    #define SHA1_HASHSIZE 20
    
    //Magic GUID as defined in RFC6455 section 1.3
    static const char magicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    
    /**
     * Compute the value of the Sec-WebSocket-Accept response header
     * from the value of the Sec-WebSocket-Key header.
     * @param key The whitespace or NUL terminated Sec-WebSocket-Key value
     * @param out Where to store the base64-encoded output. Must provide 29 bytes of memory.
     * The 29 bytes starting at out contain the resulting value (plus a terminating NUL byte)
     */
    void computeWebsocketSecAccept(const char* key, char* dst) {
        /**
         * Determine start & length of key minus leading/trailing whitespace
         * See RFC6455 section 1.3
         */
        //Skip leading whitespace
        while(isspace(*key)) {
            key++;
        }
        //Determine key size.
        size_t keySize = 0;
        while(!isspace(key[keySize]) && key[keySize] != 0) {
            keySize++;
        }
        //Compute SHA1 hash. See RFC6455 section 1.3
        char hashOut[SHA1_HASHSIZE];
        hash_state md;
    
        sha1_desc.init(&md);
        sha1_desc.process(&md, key, keySize);
        sha1_desc.process(&md, magicGUID, sizeof(magicGUID));
        sha1_desc.done(&md, hashOut);
        //Encode hash to output buffer
        size_t outlen = 29; //We know the output is 28 in size
        base64_encode(hashOut, SHA1_HASHSIZE, dst, &outlen);
    }
    
    /**
     * Usage example
     */
    int main(int argc, char** argv) {
        //Whitespace needs to be removed according to RFC6455
        //Example from RFC6455
        const char* key = " dGhlIHNhbXBsZSBub25jZQ== ";
        char buf[29];
        //Perform computation
        computeWebsocketSecAccept(key, buf);
        //Should print s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        printf("%s\n", buf);
    }