Binary Operations
Types
AND
#include <iostream>
#include <cstdint>
void bitwiseAnd() {
uint8_t a = 10; // 00001010
uint8_t b = 6; // 00000110
uint8_t c = a & b; // 00000010 (2)
std::cout << static_cast<int>(c) << std::endl;
}
OR
void bitwiseOr() {
uint8_t a = 10; // 00001010
uint8_t b = 6; // 00000110
uint8_t c = a | b; // 00001110 (14)
std::cout << static_cast<int>(c) << std::endl;
}
XOR
void bitwiseXOr() {
uint8_t a = 10; // 00001010
uint8_t b = 6; // 00000110
uint8_t c = a ^ b; // 00001100 (12)
std::cout << static_cast<int>(c) << std::endl;
}
NOT
void bitwiseNot() {
uint8_t a = 10; // 00001010
uint8_t b = ~a; // 11110101 (245)
std::cout << static_cast<int>(b) << std::endl;
}
LEFT SHIFT
void bitwiseLeftShift() {
uint8_t a = 10; // 00001010
uint8_t b = a << 2;// 00101000(40)
std::cout << static_cast<int>(b) << std::endl;
}
RIGHT SHIFT
void bitwiseRightShift() {
uint8_t a = 10; // 00001010
uint8_t b = a >> 2;// 00000010 (2)
std::cout << static_cast<int>(b) << std::endl;
}
Applications
Parity Check
- A method that adds an extra bit to the least significant bit (LSB) that tells you if there’s an even (or odd) amount of bits set.
- Depending on the system it will keep track if even or odd amount of bits were set.
- Used to check if the data recieved is not corrupt
- Limitation: only detects an odd number of errors in the number of bits set
bool parityCheck(uint8_t data) {
bool parity = false;
// Checks if each bit is set or not
for (size_t i = 0b00000000; i < 8; i++) {
// ( data & ( left Shift 00000001 every loop by 1 ))
// eg
// (00000101 & 00000100)
// = 00000100 (3rd bit was set, return true)
if (data & (1 << i)) {
parity = !parity;
}
}
return parity;
}
void check() {
// Correct data
uint8_t correctData1 = 0b01101011;
uint8_t correctData2 = 0b01001010;
// Corrupt data where the parity bit doesn't match
// the number of bits set
uint8_t corruptData1 = 0b00101011;
uint8_t corruptData2 = 0b00001010;
std::cout << parityCheck(correctData1) << std::endl; // T
std::cout << parityCheck(correctData2) << std::endl; // T
std::cout << parityCheck(corruptData1) << std::endl; // F
std::cout << parityCheck(corruptData2) << std::endl; // F
}
Boolean Algebra
- Useful to simplify expressions
AND
x & y = y & x
x & (y & z) = (x & y) & z
x & 0b11111111 = x
x & 0 = 0
x & x = x
OR
x | y = y | x
x | (y | z) = (x | y) | z
x | 0 = x
x | 0b11111111 = 0b11111111
x | x = x
XOR
x ^ y = y ^ x
x ^ (y ^ z) = (x ^ y) ^ z
x ^ 0 = x
x ^ y ^ y = x
x ^ x = 0
x ^ 0b11111111 = ~x
NOT
~(~x) = x
Mix
x | (x & y) = x
x & (x | y) = x
~(x | y) = ~x & ~y
~(x & y) = ~x | ~y
x | (y & z) = (x | y) & (x | z)
x & (y | z) = (x & y) | (x & z)
x & (y ^ z) = (x & y) ^ (x & z)
x + y = (x ^ y) + ((x & y) << 1)
x - y = ~(~x + y)
Cryptography
Circular shift
- Shifts and wraps bits around by n
- Useful to encrypt and decrypt data by knowing how many shifts are needed
0100
circular left shift by 2
0001
0001
circular right shift by 3
0010
- Below code is useful to visualise how the shift works
- There are more efficient ways to do this without looping
uint8_t circularLeftShift(uint8_t data, size_t shift) {
for (size_t i = 0; i < shift; i++)
{
// see if any bits will overflow
uint8_t left_over = data & 0b10000000;
// left shift by 1
data = data << 1;
// if there were any overflows set them to the LSB
if (left_over)
{
data = data | 0b00000001;
}
}
return data;
}
uint8_t circularRightShift(uint8_t data, size_t shift) {
for (size_t i = 0; i < shift; i++)
{
// see if any bits will overflow
uint8_t left_over = data & 0b00000001;
// right shift by 1
data = data >> 1;
// if there were any overflows set them to the MSB
if (left_over)
{
data = data | 0b10000000;
}
}
return data;
}
void runEncryption() {
uint8_t data = 0b10001001;
size_t shift = 5;
uint8_t encrypted = circularLeftShift(data, shift);
uint8_t decrypted = circularRightShift(encrypted, shift);
// 0b10001001 = 137
std::cout << static_cast<int>(data) << std::endl;
// 0b00110001 = 49
std::cout << static_cast<int>(encrypted) << std::endl;
// 0b10001001 = 137
std::cout << static_cast<int>(decrypted) << std::endl;
}
Steganography
- Hiding data within another non-secret data.
- Hiding a message in a image
- Good for hiding data discreetly
- Above picture represents a red 500 x 100 red image where every pixel is 255 0 0 (11111111 00000000 0000000)
-
Above picture represents a red 500 x 100 red image where every pixel is between 248 0 0 (11111000 0000000 0000000) and 255 0 0
-
The 3 LSB in the image above can be used to add data to the image without affecting how the image looks to the human eye
-
Above picture represents a red 500 x 100 red image where every pixel is between 192 0 0 (11000000 0000000 0000000) and 255 0 0
-
You can start to see some artifacts in the data as the more data you store
FAQ
Why is there even and odd parity?
- Essentially because legacy and standards not being made back in the day.
Appendix
# How to generate red 255 and red 248 images
import math
import random
MAGIC_NUMBER = "P3\n"
COMMENT = "# This is a rgb ppm\n"
MAX_VAL = 255
MAX_VAL_STR = f"{MAX_VAL}\n"
def write_color(filename, r_value):
image_x = 500
image_y = 100
dimension = f"{image_x} {image_y}\n"
with open(filename, "w") as f:
f.write(MAGIC_NUMBER)
f.write(COMMENT)
f.write(dimension)
f.write(MAX_VAL_STR)
for y in range(image_y):
for x in range(image_x):
f.write(f"{random.randint(r_value, 255)} 0 0 ")
f.write("\n")
# 0b11111111 = 255
write_color("red_255.ppm", 255)
# 0b11111000 = 248
write_color("red_248.ppm", 248)
# 0b11000000 = 192
write_color("red_192.ppm", 192)
Written on July 24, 2024