CS152-lecture-20210604

Bitwise manipulations #

File: Slides

image_2021-06-04-09-27-48

Data is a sequence of bytes in memory, whether we receive it from a file or network. We will use unsigned int types to avoid sign extension. These manipulations are powerful in cryptography.

image_2021-06-04-09-47-08

We can also move the bits back and forth using left/right shifts. Bits that shift off the end are gone for good, and 0s are added on the opposite end (in unsigned types).

Common uses #

image_2021-06-04-09-49-27 image_2021-06-04-09-51-47

Correction: we are clearing bit position 7 in the above slide.

x = x & ~(1 << 7);

image_2021-06-04-09-53-44

Correction: above slide should shift right 7.

x = 1 & (x >> 7);

image_2021-06-04-09-54-51

Rotations are very common in encryption.

image_2021-06-04-09-59-12

Here, (x & 0x000000FF) is called a mask. It masks everything except the last 8 bits.

image_2021-06-04-10-04-26

On Intel CPUs, the return value is left in %eax.

Array allocation #

File: Slides

image_2021-06-04-10-08-52

Auto allocated memory only exists in scope, you can’t have long lived memory this way.

image_2021-06-04-10-12-16

Remember, malloc takes the number of bytes you want, not the number of elements. If malloc is unsuccessful, it will return 0 (NULL).

image_2021-06-04-10-15-02

Reverse array example #

image_2021-06-04-10-16-25

This could crash if you have less than 400B on the stack available.

image_2021-06-04-10-19-40

This uses \( O(n) \) space complexity.

image_2021-06-04-10-22-33

Obtaining memory addresses #

image_2021-06-04-10-23-23

Inverse techniques in C #

AXR – add, XOR, rotate #

We can write an invertible functions using these 3 operations.

  • add
  • xor
  • rotation

Consider something like

x = x + y;

This can be “undone” via

x = x - y;

These work with the other operations, i.e.

x = x ^ y;
x = x ^ y;      // xor is its own inverse

Note: XOR is associative:

(x ^ y) ^ y = x ^ (y ^ y)

Rotation (we’ll use <<< to indicate left rotate):

x = x <<< y;
x = x >>> y;    // or x = x <<< (32 - y)
Note: You can either rotate back, or continue to rotate the entire width. Here we are assuming that x is a 32 bit value

We can compose these 3 operators to scramble bits (and is invertible):

uint32_t scramble(uint32_t x)
{
    x = x ^ 0x1AFE9B3A; // xor by random constant
    x = x << 12;
    x = x + 0x2783AFBC; // add random constant
    return x;
}

We could easily write the invertible function to undo these operations:

uint32_t unscramble(uint32_t x)
{
    x = x - 0x2783AFBC; // sub random constant
    x = x >> 12;
    x = x ^ 0x1AFE9B3A; // xor by random constant
    return x;
}

The operations just have to happen in the opposite order of scramble.

Feistel #

Another way to write an invertible function in C is to use a Feistel construction. The idea is that the construction itself is invertible, and not the components.

We can take an input and break it into 2 pieces, and use one half to cause a scrambling of the other half.

An abstract example in C, (here f doesn’t exist).

// x points to an array of two uint32_t
void fscramble(uint32_t * x)
{
    x[0] = x[0] ^ f(x[1])   // f returns random looking uint32_t
    x[1] = x[1] ^ f(x[0])   // f returns random looking uint32_t
}
Note: Here, f is some kind of scrambling function.

To undo these operations, we just need to do the original operations in reverse order:

// x points to an array of two uint32_t
void funscramble(uint32_t * x)
{
    x[1] = x[1] ^ f(x[0])   // f returns random looking uint32_t
    x[0] = x[0] ^ f(x[1])   // f returns random looking uint32_t
}

What is interesting about these Feistel structures, is that f itself does NOT need to be invertible.

A concrete example:

// x points to an array of two uint32_t
void fscramble(uint32_t * x)
{
    x[0] = x[0] ^ (x[1] * x[1] + x[1]) // square and add
    x[1] = x[1] ^ (x[0] * x[0] + x[0]) // square and add
}

// x points to an array of two uint32_t
void funscramble(uint32_t * x)
{
    x[1] = x[1] ^ (x[0] * x[0] + x[0]) // square and add
    x[0] = x[0] ^ (x[1] * x[1] + x[1]) // square and add
}

Composing simple ciphers #

We will make a simple function that is invertible, but simple to create. Historically, cryptography uses permutations the most for ciphers.

Caesar cipher #

One of the oldest uses of cryptography was used in Roman times, the Caesar cipher. They selected a number to be used as a fixed key. So long as the key wasn’t leaked, it provided adequate privacy.

It is a simple shift of the alphabet, so \( A \to D, B \to E \) , etc. At the end you wrap around, \( Z \to C \) .

So if we are encrypting the word BAD, and are shifting by 3:

B A D
E D G   add 3
B A D   sub 3 to get original

This is known as a shift cipher.

shift(x):
    ciphertext = x + 3 mod 26

We can make this more flexible and more secure by using the key and the character:

shift(x, k):
    ciphertext = x + k mod 26

So we can get the plaintext by doing the opposite:

plaintext = y - k mod 26
          // or
          = y + (26 - k) mod 26

This is working in the field \( Z_{26} \) , meaning we use mod 26.

The goal of this is to have the ciphertext and the function to look very random.

So if we encrypt using a key of k, and are in the field \( Z_5 \) .

k = 3
encrypt(k, x) = x + k mod 5

This is a permutation function, and all it does is add 3 to each of its inputs.

image_2021-06-04-11-01-34

This doesn’t “look random”, as it seems that all of the arrows are parallel. However, it is secure as long as k is picked at random, and we only encrypt a single x. Each of the outputs are equally likely, therefore the ciphertext is secure.

Normally however, we don’t usually encrypt a single character. We want our permutation that we are going to use to be more random so we can encrypt more than 1 char at a time.

Multiplicative cipher #

Here instead of using addition (like the shift cipher), we will use multiplication.

So if we are in \( Z_{26} \) .

\[\begin{aligned} e(k,x) &= xk \mod 26 \\ d(k,y) &= y k^{-1} \mod 26 \end{aligned}\]

This only works if the multiplicative inverse \( k^{-1} \) exists in \( Z_{26} \) . The inverse of \( k \) has to be an element in \( Z_{26} \) , normally people think of fractions as inverses.

Note that not all values in \( Z_{26} \) have an inverse.

\( k^{-1} \) exists iff \( gcd(k,26) = 1 \) .

If \( k \) and \( 26 \) share no factors, then it has a multiplicative inverse.

The easiest way to find candidates for \( k \) is:

  1. Go through all of the numbers in \( Z_{26} \) and throw out the prime factors that have 26.

So 26 has two prime factors: \( 26 = 2 * 13 \) .

So we throw out all the ones that have a multiple of 2 or 13, whats left is the numbers that have a multiplicative inverse, i.e.

\[ k = 1,3,5,7,9,11,15,17,19,21 \]

So lets see how random the multiplicative cipher looks compared to the shift cipher:

If \( k = 3 \) and \( Z_5 \to Z_5 \) :

\( E(k,x) = kx \mod 5 \)
x E(3,x)
0 0
1 3
2 1
3 4
4 2

image_2021-06-04-11-17-04

This looks a lot more random than the shift cipher. However, it looks like 0 will always map to 0 in a multiplicative cipher. Also, even though the arrows aren’t parallel, they still have some regularity to them. So we can do better.

Composing ciphers #

Principle: Composing simple ciphers of different structures yields a more random looking cipher.

If we compose 2 invertible functions, the composition will still be invertible.

For example, if we start with a shift cipher, and then use a multiplicative cipher:

image_2021-06-04-11-22-52

Correction: On right, 2 goes to 2, and 4 goes to 3.

You need to alternate between different ciphers to add security. (Composing 2 shifts wouldn’t add any additional security than 1).

Example of a non-linear simple cipher #

An example of a non-linear cipher is a transposition cipher.

It considers the data to be in chunks, it doesn’t scramble the characters, but rearranges them:

image_2021-06-04-11-27-51

It is a simple cipher (because it is reversible), and it is a permutation (domain and codomain are same). It is non-linear because it cannot be represented by the sum of product terms.

Connection to AXR operations:

  • add is a shift cipher in \( Z_{2^n} \)
  • xor is a shift cipher in \( Z_2 \), for example \(a \oplus b = a + b \mod 2\)
  • rotate is a transposition.

We usually don’t use multiplication because it is more computationally expensive.