diff --git a/Makefile b/Makefile
index 25b983d..da37ddd 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ CC := $(TOOL_PREFIX)-gcc
 OBJDUMP := $(TOOL_PREFIX)-objdump
 OBJCOPY := $(TOOL_PREFIX)-objcopy
 
-SRC := ch32fun.c main.c
+SRC := ch32fun.c main.c rsa.c rand.c
 
 CFLAGS = -g \
 		 -Os \
diff --git a/main.c b/main.c
index f7fded8..4128657 100644
--- a/main.c
+++ b/main.c
@@ -1,9 +1,10 @@
 #include <ch32fun.h>
+#include <rand.h>
 #include <rsa.h>
+#include <stdint.h>
+#include <stdio.h>
 
 #define LED_PIN PD6
-#define FREQ 2
-#define BLINK_DELAY 1000 / FREQ
 
 void exit_blink() {
     for (int i = 0; i < 4; i++) {
@@ -12,28 +13,53 @@ void exit_blink() {
         funDigitalWrite(LED_PIN, FUN_LOW);
         Delay_Ms(50);
     }
+}
 
-    while (1){};
-
-    funDigitalWrite(LED_PIN, FUN_HIGH);
+void enter_blink() {
+    for (int i = 0; i < 2; i++) {
+        funDigitalWrite(LED_PIN, FUN_HIGH);
+        Delay_Ms(200);
+        funDigitalWrite(LED_PIN, FUN_LOW);
+        Delay_Ms(200);
+    }
 }
 
 int main() {
     SystemInit();
 
-    // Enable GPIOs
-    funGpioInitAll();
+    printf("Entering...");
 
+    funGpioInitAll();
     funPinMode(LED_PIN, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
 
-    int i = 0;
-    while (i < gcd(930, 10)) {
-        i++;
-        funDigitalWrite(LED_PIN, FUN_HIGH);
-        Delay_Ms(BLINK_DELAY);
-        funDigitalWrite(LED_PIN, FUN_LOW);
-        Delay_Ms(BLINK_DELAY);
+    enter_blink();
+
+    uint64_t p = gen_prime(1 << 15, 1 << 16);
+    uint64_t q = gen_prime(1 << 15, 1 << 16);
+
+    while (p == q) p = gen_prime(1 << 15, 1 << 16);
+
+    uint64_t n = p * q;
+
+    // Make these work by patching printf
+    printf("P: %llu\n", p);
+    printf("Q: %llu\n", q);
+    printf("N: %llu\n", n);
+
+    for (int idx = 0; idx < 16; idx++) {
+        funDigitalWrite(LED_PIN, p >> idx & 1);
+        Delay_Ms(200);
+    }
+    for (int idx = 0; idx < 16; idx++) {
+        funDigitalWrite(LED_PIN, q >> idx & 1);
+        Delay_Ms(200);
+    }
+    for (int idx = 0; idx < 16; idx++) {
+        funDigitalWrite(LED_PIN, n >> idx & 1);
+        Delay_Ms(200);
     }
 
+    // Exit and hang forever
     exit_blink();
+    while (1);
 }
diff --git a/rsa.c b/rsa.c
new file mode 100644
index 0000000..07e06bd
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,81 @@
+#include "rsa.h"
+#include "rand.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+int gcd(int a, int b) {
+    while (b != 0) {
+        int temp = b;
+        b = a % b;
+        a = temp;
+    }
+
+    return a;
+}
+
+int totient(int n) {
+    int result = n;
+
+    // Check for prime factors
+    for (int p = 2; p * p <= n; p++) {
+        if (n % p == 0) {
+            // If p is a prime factor of n, remove all occurrences of p
+            while (n % p == 0) {
+                n /= p;
+            }
+            result -= result / p;
+        }
+    }
+
+    // If n is still greater than 1, then it's a prime factor itself
+    if (n > 1) {
+        result -= result / n;
+    }
+
+    return result;
+}
+
+uint64_t modexp(uint64_t a, uint64_t b, uint64_t m) {
+    uint64_t result = 1;
+    a = a % m; // In case a is greater than m
+
+    while (b > 0) {
+        // If b is odd, multiply a with result
+        if (b % 2 == 1)
+            result = (result * a) % m;
+
+        // b must be even now
+        b = b >> 1;      // b = b // 2
+        a = (a * a) % m; // Change a to a^2
+    }
+
+    return result;
+}
+
+uint64_t gen_prime(uint64_t min, uint64_t max) {
+    uint64_t cand = 0;
+    while (!is_prime(cand)) cand = prand_range(min, max);
+
+    return cand;
+}
+
+bool is_prime(int n) {
+    if (n < 2)
+        return false;
+
+    for (int i = 2; i < n / 2 + 1; i++) {
+        if (n % i == 0)
+            return false;
+    }
+
+    return true;
+}
+
+int mod_inverse(int e, int phi) {
+    for (int d = 0; d < phi; d++) {
+        if ((d * e) % phi == 1)
+            return d;
+    }
+
+    return 0;
+}
diff --git a/rsa.h b/rsa.h
new file mode 100644
index 0000000..b84c43d
--- /dev/null
+++ b/rsa.h
@@ -0,0 +1,57 @@
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * @brief Calculates greatest common divider of two integers using the euclidean
+ * algorithm
+ *
+ * @param a First number
+ * @param b Second number
+ * @return The greatest common divider
+ */
+int gcd(int a, int b);
+
+/**
+ * @brief Computes Euler's Totient function φ(n), which counts the number of
+ *        integers from 1 to n that are coprime to n.
+ *
+ * @param n The input number.
+ * @return The number of integers from 1 to n that are coprime to n.
+ */
+int totient(int n);
+
+/**
+ * @brief Modular exponentiation (a^b) mod m
+ *
+ * @param a The base
+ * @param b The exponent
+ * @param m The modulus
+ */
+uint64_t modexp(uint64_t a, uint64_t b, uint64_t m);
+
+/**
+ * @brief Computes the modular inverse of e modulo phi.
+ *
+ * @param e The integer whose modular inverse is to be found.
+ * @param phi The modulus.
+ * @return The modular inverse of e modulo phi, or -1 if no inverse exists.
+ */
+int mod_inverse(int e, int phi);
+
+/**
+ * @brief Generates a random prime number within the given range.
+ *
+ * @param min The lower bound (inclusive).
+ * @param max The upper bound (inclusive).
+ * @return A prime number in the range [min, max].
+ */
+uint64_t gen_prime(uint64_t min, uint64_t max);
+
+/**
+ * @brief Checks if a number is prime.
+ *
+ * @param n The number to check.
+ * @return true if n is prime, false otherwise.
+ */
+bool is_prime(int n);