Lecture 25 XOR Cipher Lab

Joseph Haugh

University of New Mexico

Free Recall

Review Question 1

struct Point {
    int x;
    int y;
};
void movePoint(struct Point p, int dx, int dy) {
    p.x += dx;
    p.y += dy;
}
void main() {
    struct Point p = {1, 2};
    printf("Before move: (%d, %d)\n", p.x, p.y);
    movePoint(p, 3, 4);
    printf("After move: (%d, %d)\n", p.x, p.y);
}
Before move: (1, 2)
After move: (1, 2)

Review Question 2

struct Rectangle {
    int length;
    int width;
};
void doubleSize(struct Rectangle *r) {
    r->length *= 2;
    r->width *= 2;
}
void main() {
    struct Rectangle r = {5, 3};
    printf("Before doubling: %d x %d\n", r.length, r.width);
    doubleSize(&r);
    printf("After doubling: %d x %d\n", r.length, r.width);
}
Before doubling: 5 x 3
After doubling: 10 x 6

Review Question 3

struct Rectangle {
    int length;
    int width;
};
struct Rectangle addRects(struct Rectangle r1, struct Rectangle r2) {
    struct Rectangle r = { r1.length, r1. width };
    r.length += r2.length;
    r.width  += r2.width;
    return r;
}
void main() {
    struct Rectangle r1 = {5, 3};
    struct Rectangle r2 = {4, 6};
    struct Rectangle r3 = addRects(r1, r2);
    printf("r3.length = %d, r3.width = %d\n", r3.length, r3.width);
}
r3.length = 9, r3.width = 9

Review Question 4

struct Rectangle {
    int length;
    int width;
};
struct Rectangle *addRects(struct Rectangle r1, struct Rectangle r2) {
    struct Rectangle r = { r1.length, r1. width };
    r.length += r2.length;
    r.width  += r2.width;
    return &r;
}
void main() {
    struct Rectangle r1 = {5, 3};
    struct Rectangle r2 = {4, 6};
    struct Rectangle *r3 = addRects(r1, r2);
    printf("r3.length = %d, r3.width = %d\n", r3->length, r3->width);
}
Segmentation fault (core dumped)

Review Question 5

void main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr;
    *(ptr++) += 5;
    *(++ptr) += 10;
    ptr[2] += 15;
    printf("%d %d %d %d %d\n", 
           arr[0], arr[1], arr[2], arr[3], arr[4]);
}
15 20 40 40 65

Review Question 6

void main() {
    int a = 10;
    int *p = &a;
    int **q = &p;
    **q = 20;
    *q = NULL;
    printf("a = %d\n", a);
    printf("p = %p\n", p);
}
a = 20
p = (nil)

XOR Cipher Lab

The cipher used in this project:

  • Encrypts plain text messages consisting solely of the printable ASCII characters.

  • Decrypts ciphertext consisting solely of the printable ASCII characters.

  • The printable ASCII characters are 8-bit codes in the range of values from 32 to 126 (00100000 through 01111110). See ASCII Wikipedia

  • Any 8-bit sequences outside of this range constitutes invalid data.

Cipher Record Format

Each cipher record must be of the form:

Action lcg_m , lcg_c , Data
1 char 1-20 char 1 char 1-20 char 1 char any # of char 1 char
  • [Action:] Must be either ‘e’ or ‘d’ specifying encryption or decryption respectively.

  • [lcg_m:] Specifies a 64-bit positive integer used for m of a Linear Congruential Generator. Must be decimal digits.

  • [lcg_c:] Specifies a 64-bit positive integer used for c of a Linear Congruential Generator. Must be decimal digits.

  • [Data:] Printable ASCII character data to be encrypted or decrypted. Can be empty or arbitrarily long.

Cipher Algorithm: Summary

  1. Determine the Linear Congruential Generator specified by the given key. This is done only once per line of input.

  2. Read 1 byte of data b

  3. Generate random value x

  4. Compute encrypted byte as b XOR (x mod 128)

  5. Deal with any non-printable ASCII characters.

  6. Print the resulting character(s) to the standard output stream.

  7. Return to step 2 and continue until the end of the line

Cipher Algorithm: Initialization

Given a 128-bit symmetric key consisting of:

  • m : a 64-bit positive integer used as an LCG modulus,

  • c : a 64-bit positive integer used as an LCG increment.

Define a Linear Congruential Generator:

Xn + 1 = (aXn + c) mod  m

  • $X_0 = c $

  • a = 1 + 2p, if 4 is a factor of m, otherwise, a = 1 + p.

  • p= product of m’s unique prime factors

Reading Data Bytes

  • Within each record, the data portion is the set of characters between the second comma and '\n'.

  • When encrypting: Use getchar() to read 1 byte of data to encrypt.

  • When decrypting: Reading in an encrypted byte may require reading 2 bytes from standard input since some character codes have 2-byte ciphertext representations.

  • Any character in the data segment not in the range of printable ASCII characters (32 to 126) is an error.

Non-printable ASCII characters

This cipher algorithm can generate target bytes that are outside the range of printable ASCII.

  • When encrypting, if a generated byte e is:

    Condition Action
     < 32 Replace with two bytes: '\*' and '?'+e.
     = 127 Replace with two bytes: '\*' and '$'.
    = '\*' Replace with two bytes: '\*' and '\*'.
  • When decrypting, if a generated byte p is a non-printing ASCII character, then print the specified error message and read to the end of line.

Output Format

  • For every line of input, one line of output must be sent to the standard output stream.

  • If the input line is invalid, then the output has the form:

    ("%5d: %s Error\n", inputLineNumber, trash)

    where trash is any string no longer than twice the length of any data part of the line.

  • If the input line is valid, then the output has the form:

    ("%5d: %s\n", inputLineNumber, outStr)

    Where outStr is the encrypted or decrypted data.

Example: Encrypt

Let’s walk through an example of encrypting step by step.

Example: Read Line

Input: e126,25,Byte\n

  • Given Key: m = 126 and c = 25

  • Calculate: a = 43
    (2 × 3 × 7 + 1 since m = 126 = 2 × 3 × 3 × 7)

  • LCG: Xn + 1 = (43(Xn) + 25) mod  126 with X0 = 25

  • Data: Byte

B 0 1 0 0 0 0 1 0
y 0 1 1 1 1 0 0 1
t 0 1 1 1 0 1 0 0
e 0 1 1 0 0 1 0 1

LCG Example: Generating random values

You’ll be generating one value at a time in your program, I’m just demonstrating the sequence here.

In this example, we have Xi mod  128 = Xi since m = 126 < 128

This will not be the case with larger LCG values.

i Xi Xi mod  128
0 25 25
1 92 92
2 75 75
3 100 100
4 41 41
5 24 24
6 49 49
7 116 116

Example: Encrypting

Data: B 66 0 1 0 0 0 0 1 0
Xi mod  128 25 0 0 0 1 1 0 0 1
encrypted [ 91 0 1 0 1 1 0 1 1
Data: y 121 0 1 1 1 1 0 0 1
Xi mod  128 92 0 1 0 1 1 1 0 0
encrypted % 37 0 0 1 0 0 1 0 1

Example: Encrypting

Data: t 116 0 1 1 1 0 1 0 0
Xi mod  128 75 0 1 0 0 1 0 1 1
encrypted ? 63 0 0 1 1 1 1 1 1
Data: e 101 0 1 1 0 0 1 0 1
Xi mod  128 100 0 1 1 0 0 1 0 0
encrypted *@ 1 0 0 0 0 0 0 0 1

Byte encrypts to [%?*@

Example: Decrypt

Let’s walk through an example of decrypting step by step.

Example: Read Line

Input: d256,123,9*Z*P*P\n

  • Given Key: m = 256 and c = 123

  • Calculate: a = 5
    (2 × 2 + 1 since m = 256 = 28)

  • LCG: Xn + 1 = (5(Xn) + 123) mod  126 with X0 = 123

  • Data: 9*Z*P*P

Example: Data

9 0 0 1 1 1 0 0 1
*Z 0 0 0 1 1 0 1 1
*P 0 0 0 1 0 0 0 1
*P 0 0 0 1 0 0 0 1
ASCII
'9' 57
'?' 63
'Z' 90 'Z' - '?' is 27
'P' 80 'P' - '?' is 17

LCG Example: Generating random values

You’ll be generating one value at a time in your program, I’m just demonstrating the sequence here.

This time around, the modulus operation actually matters, since 256 > 128

i Xi Xi mod  128
0 123 123
1 226 98
2 229 101
3 244 116

Example: Decrypting

Data: 9 57 0 0 1 1 1 0 0 1
Xi mod  128 123 0 1 1 1 1 0 1 1
decrypted B 66 0 1 0 0 0 0 1 0
Data: *Z 27 0 0 0 1 1 0 1 1
Xi mod  128 98 0 1 1 0 0 0 1 0
decrypted y 121 0 1 1 1 1 0 0 1

Example: Decrypting

Data: *P 17 0 0 0 1 0 0 0 1
Xi mod  128 101 0 1 1 0 0 1 0 1
decrypted t 116 0 1 1 1 0 1 0 0
Data: *P 17 0 0 0 1 0 0 0 1
Xi mod  128 116 0 1 1 1 0 1 0 0
decrypted e 101 0 1 1 0 0 1 0 1

9*Z*P*P decrypts to Byte

Finding Unique Prime Factors - Algorithm

  1. Let n be the number of which to find the prime factors.

  2. Start with 2 as a test divisor.

  3. If the test divisor squared is greater than the current n, then the current n is either 1 or prime. Save it if prime and return.

  4. If the remainder of n divided by the test divisor is zero, then:

    1. The test divisor is a prime factor of n. Save it.

    2. Replace n with n divided by the test divisor.

    3. Repeat b until test divisor is no longer a factor of n.

  5. If, in step 4, the remainder of n divided by the test divisor was not zero, then increment the test divisor. Loop to step 3.

How Many Prime Factors Can n have?

Write a function that finds the unique prime factors of a number, n, and stores each factor in an array. How big does the array need to be?

  • If n is a 64-bit integer then the largest number it can hold is 264 − 1.

  • Since 2 is the smallest prime, the largest 64-bit number must have less than 64 factors.

  • But only Unique Prime Factors are needed.

  • The product of the first 15 primes is:

    2 × 3 × 5 × 7 × 11 × 13 × 17 × 19 × 23 × 29 × 31 × 37 × 41 × 43 × 47 = 11, 682, 905, 869, 181, 336, 790.
    Times 53 > 264 − 1.