Home

TLS-1.3

TLS-1.3 protocol written in C without any non-standard libraries. It implements algorithms such as AES in GCM, RSA, x25519 An elliptic curve key exchange algorithm on Curve25519 , and SHA-256. To handle the large numbers involved in cryptography, the project has a custom big number library. It follows the rough idea of the specification and all the main components required for secure communication but does not follow all the details of RFC 8446 .

Demo

Under construction

Interesting Things

Big Number Library

Using the large numbers in cryptography are a bit of a pain. Most computers can't handle integers greater than 64 bits and so operations on numbers which can be 2048 bits becomes a bit troublesome. Without using external libraries (the sensible choice), the solution is to treat them as arrays. However, you now have to implement all the arithemtic functions for this new type. Additionally, in C, you have to handle the memory allocations in which these variable-sized numbers sit (and avoid allocations where performance is important). This is what bigmaths.c does. Something like == becomes
typedef uint32_t* bignum;

uint8 bigNumCmp(bignum a,int lenA,bignum b,int lenB){
    int lenLongest = max(lenA,lenB);
    int iA,iB;
    uint32_t op1,op2;
    for(int i=0;i<lenLongest;i++){
        iA = lenA - (lenLongest-i);
        iB = lenB - (lenLongest-i);
        op1 = (iA<0)?0:a[iA];
        op2 = (iB<0)?0:b[iB];
        if(op1<op2) return LESS_THAN;
        else if(op1>op2) return GREATER_THAN;
    }
    return EQUAL;
}

Difficulty of implementing stuff safely

TLS standard provides the structure needed for confidentiality, message integrity, and authentication but writing poor code can undermine these. Some of the interesting things I had to consider were:
  • Constant time algorithms

    Algorithms can scale with variables such as the number of bits. However, they must take the same amount of time when two numbers are the same size. If your secret is 1 and the computer takes a lot less time than when it manipulates a secret of \(2^256 -1\), then the attacker has gained information on your secret and, depending on how non-constant it is, may be able to determine your secret; not ideal. This is a real issue for stuff such as exponentiation where the common algorithm is square and multiply Too long for this margin Wikipedia where the naive implementation will do \(b\) extra multiplications where \(b\) is the number of 1 bits in the number. To rectify this vulnerability, you perform square and always multiply. Where at each bit of the exponent, you perform a square and a multiply, regardless of if you need the multiplication or not.

  • Small group confinement

    In some elliptic curve groups, the order of the group is non-prime and so by Lagrange's theorem, there could exist a subgroup which has an order of a smaller factor of the original group's order. On Curve25519, the group used has an order of \(8p\) where \(p\) is a very large prime. During a key exchange, an interceptor (Eve of course) could perform point multiplication on the first step of the key share (\(G^a\)) to bring it into a subgroup of order 8 (i.e. \({G^a}^p\)). When the recipient uses their secret to produce the shared secret, they can only produce 8 possible private keys and Eve can brute force this. To combat this, the recipient can 'clamp' their private key (the \(b\) in \({G^a}^b\)) by zeroing out the lowest 3 bits. As \(b\) is a multiple of 8, if \(G^a\) was in a subgroup of order 8, then \({G^a}^b\) is now the identity of the group. This can be spotted and the result discarded.

  • Deterministic random numbers

    See below

My code attempts to account for these issues but is not perfect. So, along with other reasons, you should not use this code for anything serious.

Unit tests

They sound very boring (especially for a personal project) but are quite useful. They seem to slow down development but remove a large chunk of "how on Earth can that happen" after hastily writing a few thousand lines of code. Probably faster developing with unit tests than without. I used GoogleTest (towards the end) which is quite pleasant to use.

Random numbers

For cryptography you want something actually unpredictable as otherwise the secrets aren't so secret. However, computer doesn't do much randomness. Random numbers often use algorithms such as the Mersenne Twister but these are deterministic sequences and require a seed value to start from which puts you back at the same problem of finding a random number. Cryptographical seeds usually use hardware sources which are quite hard to predict. My favourite source is mouse wiggling but this is a bit inconvenient when you have to generate multiple random numbers. My program uses: the current time in nanoseconds, the time the kernel and user have been running for, the amount of free physical and virtual memory, the process and thread IDs, the number of CPU cycles, the current stack address and the current mouse position. It XORs these together and then hashes it to diffuse the randomness.