feat: wi-fi - join WPA2/WPA3-PSK networks and carry traffic like ethernet

This commit is contained in:
daniel committed 2026-08-06 19:44:35 +02:00
1 parent a01e63c717
commit bbe1df62fd
40 files changed
+5878 -272

No files matched your search

+700
View File
@@ -0,0 +1,700 @@
/*
* Crypto.cpp
* SHA-1, SHA-256, HMAC, PBKDF2, AES and AES-CMAC for the Wi-Fi supplicant.
* See Crypto.hpp for why these live in the kernel.
* Copyright (c) 2026 Daniel Hammer
*/
#include "Crypto.hpp"
#include <Libraries/Memory.hpp>
namespace Kt::Crypto {
// =========================================================================
// Small helpers
// =========================================================================
static inline uint32_t Rol32(uint32_t v, int n) {
return (v << n) | (v >> (32 - n));
}
static inline uint32_t Ror32(uint32_t v, int n) {
return (v >> n) | (v << (32 - n));
}
static inline uint32_t LoadBe32(const uint8_t* p) {
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16)
| ((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
static inline void StoreBe32(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)(v >> 24); p[1] = (uint8_t)(v >> 16);
p[2] = (uint8_t)(v >> 8); p[3] = (uint8_t)v;
}
static inline void StoreBe64(uint8_t* p, uint64_t v) {
StoreBe32(p, (uint32_t)(v >> 32));
StoreBe32(p + 4, (uint32_t)v);
}
void SecureZero(void* p, size_t len) {
volatile uint8_t* q = (volatile uint8_t*)p;
while (len--) *q++ = 0;
}
bool SecureEqual(const void* a, const void* b, size_t len) {
const uint8_t* x = (const uint8_t*)a;
const uint8_t* y = (const uint8_t*)b;
uint8_t diff = 0;
for (size_t i = 0; i < len; i++) diff |= (uint8_t)(x[i] ^ y[i]);
return diff == 0;
}
// =========================================================================
// SHA-1
// =========================================================================
static void Sha1Block(uint32_t* st, const uint8_t* block) {
uint32_t w[80];
for (int i = 0; i < 16; i++) w[i] = LoadBe32(block + i * 4);
for (int i = 16; i < 80; i++)
w[i] = Rol32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
uint32_t a = st[0], b = st[1], c = st[2], d = st[3], e = st[4];
for (int i = 0; i < 80; i++) {
uint32_t f, k;
if (i < 20) { f = (b & c) | (~b & d); k = 0x5A827999; }
else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
else { f = b ^ c ^ d; k = 0xCA62C1D6; }
uint32_t t = Rol32(a, 5) + f + e + k + w[i];
e = d; d = c; c = Rol32(b, 30); b = a; a = t;
}
st[0] += a; st[1] += b; st[2] += c; st[3] += d; st[4] += e;
}
void Sha1Init(Sha1Ctx& ctx) {
ctx.State[0] = 0x67452301; ctx.State[1] = 0xEFCDAB89;
ctx.State[2] = 0x98BADCFE; ctx.State[3] = 0x10325476;
ctx.State[4] = 0xC3D2E1F0;
ctx.Count = 0;
ctx.Partial = 0;
}
void Sha1Update(Sha1Ctx& ctx, const void* data, size_t len) {
const uint8_t* p = (const uint8_t*)data;
ctx.Count += len;
if (ctx.Partial) {
uint32_t need = SHA1_BLOCK_SIZE - ctx.Partial;
uint32_t take = (len < need) ? (uint32_t)len : need;
memcpy(ctx.Buffer + ctx.Partial, p, take);
ctx.Partial += take;
p += take;
len -= take;
if (ctx.Partial < SHA1_BLOCK_SIZE) return;
Sha1Block(ctx.State, ctx.Buffer);
ctx.Partial = 0;
}
while (len >= SHA1_BLOCK_SIZE) {
Sha1Block(ctx.State, p);
p += SHA1_BLOCK_SIZE;
len -= SHA1_BLOCK_SIZE;
}
if (len) {
memcpy(ctx.Buffer, p, len);
ctx.Partial = (uint32_t)len;
}
}
void Sha1Final(Sha1Ctx& ctx, uint8_t out[SHA1_DIGEST_SIZE]) {
uint64_t bits = ctx.Count * 8;
ctx.Buffer[ctx.Partial++] = 0x80;
if (ctx.Partial > SHA1_BLOCK_SIZE - 8) {
memset(ctx.Buffer + ctx.Partial, 0, SHA1_BLOCK_SIZE - ctx.Partial);
Sha1Block(ctx.State, ctx.Buffer);
ctx.Partial = 0;
}
memset(ctx.Buffer + ctx.Partial, 0, SHA1_BLOCK_SIZE - 8 - ctx.Partial);
StoreBe64(ctx.Buffer + SHA1_BLOCK_SIZE - 8, bits);
Sha1Block(ctx.State, ctx.Buffer);
for (int i = 0; i < 5; i++) StoreBe32(out + i * 4, ctx.State[i]);
SecureZero(ctx.Buffer, sizeof(ctx.Buffer));
}
void Sha1(const void* data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]) {
Sha1Ctx ctx;
Sha1Init(ctx);
Sha1Update(ctx, data, len);
Sha1Final(ctx, out);
}
// =========================================================================
// SHA-256
// =========================================================================
static const uint32_t kSha256K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};
static void Sha256Block(uint32_t* st, const uint8_t* block) {
uint32_t w[64];
for (int i = 0; i < 16; i++) w[i] = LoadBe32(block + i * 4);
for (int i = 16; i < 64; i++) {
uint32_t s0 = Ror32(w[i - 15], 7) ^ Ror32(w[i - 15], 18) ^ (w[i - 15] >> 3);
uint32_t s1 = Ror32(w[i - 2], 17) ^ Ror32(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
uint32_t a = st[0], b = st[1], c = st[2], d = st[3];
uint32_t e = st[4], f = st[5], g = st[6], h = st[7];
for (int i = 0; i < 64; i++) {
uint32_t S1 = Ror32(e, 6) ^ Ror32(e, 11) ^ Ror32(e, 25);
uint32_t ch = (e & f) ^ (~e & g);
uint32_t t1 = h + S1 + ch + kSha256K[i] + w[i];
uint32_t S0 = Ror32(a, 2) ^ Ror32(a, 13) ^ Ror32(a, 22);
uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
uint32_t t2 = S0 + maj;
h = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
st[0] += a; st[1] += b; st[2] += c; st[3] += d;
st[4] += e; st[5] += f; st[6] += g; st[7] += h;
}
void Sha256Init(Sha256Ctx& ctx) {
ctx.State[0] = 0x6a09e667; ctx.State[1] = 0xbb67ae85;
ctx.State[2] = 0x3c6ef372; ctx.State[3] = 0xa54ff53a;
ctx.State[4] = 0x510e527f; ctx.State[5] = 0x9b05688c;
ctx.State[6] = 0x1f83d9ab; ctx.State[7] = 0x5be0cd19;
ctx.Count = 0;
ctx.Partial = 0;
}
void Sha256Update(Sha256Ctx& ctx, const void* data, size_t len) {
const uint8_t* p = (const uint8_t*)data;
ctx.Count += len;
if (ctx.Partial) {
uint32_t need = SHA256_BLOCK_SIZE - ctx.Partial;
uint32_t take = (len < need) ? (uint32_t)len : need;
memcpy(ctx.Buffer + ctx.Partial, p, take);
ctx.Partial += take;
p += take;
len -= take;
if (ctx.Partial < SHA256_BLOCK_SIZE) return;
Sha256Block(ctx.State, ctx.Buffer);
ctx.Partial = 0;
}
while (len >= SHA256_BLOCK_SIZE) {
Sha256Block(ctx.State, p);
p += SHA256_BLOCK_SIZE;
len -= SHA256_BLOCK_SIZE;
}
if (len) {
memcpy(ctx.Buffer, p, len);
ctx.Partial = (uint32_t)len;
}
}
void Sha256Final(Sha256Ctx& ctx, uint8_t out[SHA256_DIGEST_SIZE]) {
uint64_t bits = ctx.Count * 8;
ctx.Buffer[ctx.Partial++] = 0x80;
if (ctx.Partial > SHA256_BLOCK_SIZE - 8) {
memset(ctx.Buffer + ctx.Partial, 0, SHA256_BLOCK_SIZE - ctx.Partial);
Sha256Block(ctx.State, ctx.Buffer);
ctx.Partial = 0;
}
memset(ctx.Buffer + ctx.Partial, 0, SHA256_BLOCK_SIZE - 8 - ctx.Partial);
StoreBe64(ctx.Buffer + SHA256_BLOCK_SIZE - 8, bits);
Sha256Block(ctx.State, ctx.Buffer);
for (int i = 0; i < 8; i++) StoreBe32(out + i * 4, ctx.State[i]);
SecureZero(ctx.Buffer, sizeof(ctx.Buffer));
}
void Sha256(const void* data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]) {
Sha256Ctx ctx;
Sha256Init(ctx);
Sha256Update(ctx, data, len);
Sha256Final(ctx, out);
}
// =========================================================================
// HMAC
// =========================================================================
void HmacSha1(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[SHA1_DIGEST_SIZE]) {
uint8_t k[SHA1_BLOCK_SIZE] = {};
if (keyLen > SHA1_BLOCK_SIZE) {
Sha1(key, keyLen, k);
} else {
memcpy(k, key, keyLen);
}
uint8_t pad[SHA1_BLOCK_SIZE];
Sha1Ctx ctx;
for (int i = 0; i < (int)SHA1_BLOCK_SIZE; i++) pad[i] = (uint8_t)(k[i] ^ 0x36);
Sha1Init(ctx);
Sha1Update(ctx, pad, SHA1_BLOCK_SIZE);
for (int i = 0; i < count; i++) Sha1Update(ctx, parts[i], lens[i]);
uint8_t inner[SHA1_DIGEST_SIZE];
Sha1Final(ctx, inner);
for (int i = 0; i < (int)SHA1_BLOCK_SIZE; i++) pad[i] = (uint8_t)(k[i] ^ 0x5c);
Sha1Init(ctx);
Sha1Update(ctx, pad, SHA1_BLOCK_SIZE);
Sha1Update(ctx, inner, SHA1_DIGEST_SIZE);
Sha1Final(ctx, out);
SecureZero(k, sizeof(k));
SecureZero(pad, sizeof(pad));
SecureZero(inner, sizeof(inner));
}
void HmacSha1(const uint8_t* key, size_t keyLen,
const void* data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]) {
const uint8_t* p = (const uint8_t*)data;
HmacSha1(key, keyLen, &p, &len, 1, out);
}
void HmacSha256(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[SHA256_DIGEST_SIZE]) {
uint8_t k[SHA256_BLOCK_SIZE] = {};
if (keyLen > SHA256_BLOCK_SIZE) {
Sha256(key, keyLen, k);
} else {
memcpy(k, key, keyLen);
}
uint8_t pad[SHA256_BLOCK_SIZE];
Sha256Ctx ctx;
for (int i = 0; i < (int)SHA256_BLOCK_SIZE; i++) pad[i] = (uint8_t)(k[i] ^ 0x36);
Sha256Init(ctx);
Sha256Update(ctx, pad, SHA256_BLOCK_SIZE);
for (int i = 0; i < count; i++) Sha256Update(ctx, parts[i], lens[i]);
uint8_t inner[SHA256_DIGEST_SIZE];
Sha256Final(ctx, inner);
for (int i = 0; i < (int)SHA256_BLOCK_SIZE; i++) pad[i] = (uint8_t)(k[i] ^ 0x5c);
Sha256Init(ctx);
Sha256Update(ctx, pad, SHA256_BLOCK_SIZE);
Sha256Update(ctx, inner, SHA256_DIGEST_SIZE);
Sha256Final(ctx, out);
SecureZero(k, sizeof(k));
SecureZero(pad, sizeof(pad));
SecureZero(inner, sizeof(inner));
}
void HmacSha256(const uint8_t* key, size_t keyLen,
const void* data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]) {
const uint8_t* p = (const uint8_t*)data;
HmacSha256(key, keyLen, &p, &len, 1, out);
}
// =========================================================================
// PBKDF2-HMAC-SHA1
// =========================================================================
void Pbkdf2Sha1(const char* password, size_t passLen,
const uint8_t* salt, size_t saltLen,
uint32_t iterations, uint8_t* out, size_t outLen) {
const uint8_t* pw = (const uint8_t*)password;
uint32_t block = 1;
while (outLen > 0) {
uint8_t counter[4] = {
(uint8_t)(block >> 24), (uint8_t)(block >> 16),
(uint8_t)(block >> 8), (uint8_t)block
};
const uint8_t* parts[2] = { salt, counter };
size_t lens[2] = { saltLen, 4 };
uint8_t u[SHA1_DIGEST_SIZE];
uint8_t acc[SHA1_DIGEST_SIZE];
HmacSha1(pw, passLen, parts, lens, 2, u);
memcpy(acc, u, SHA1_DIGEST_SIZE);
for (uint32_t i = 1; i < iterations; i++) {
HmacSha1(pw, passLen, u, SHA1_DIGEST_SIZE, u);
for (int j = 0; j < (int)SHA1_DIGEST_SIZE; j++) acc[j] ^= u[j];
}
size_t take = outLen < SHA1_DIGEST_SIZE ? outLen : SHA1_DIGEST_SIZE;
memcpy(out, acc, take);
out += take;
outLen -= take;
block++;
SecureZero(u, sizeof(u));
SecureZero(acc, sizeof(acc));
}
}
// =========================================================================
// AES
// =========================================================================
static const uint8_t kSbox[256] = {
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
};
static const uint8_t kRsbox[256] = {
0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d,
};
// Round constants; indexed by (i / Nk), which starts at 1.
static const uint8_t kRcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
static inline uint8_t Xtime(uint8_t x) {
return (uint8_t)((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// Galois-field multiply, used only by the inverse mix-columns step.
static uint8_t GfMul(uint8_t x, uint8_t y) {
uint8_t r = 0;
for (int i = 0; i < 8; i++) {
if (y & 1) r ^= x;
uint8_t hi = (uint8_t)(x & 0x80);
x <<= 1;
if (hi) x ^= 0x1b;
y >>= 1;
}
return r;
}
bool AesInit(AesCtx& ctx, const uint8_t* key, size_t keyLen) {
int nk, nr;
if (keyLen == 16) { nk = 4; nr = 10; }
else if (keyLen == 32) { nk = 8; nr = 14; }
else return false;
ctx.Rounds = nr;
uint8_t* rk = ctx.RoundKey;
memcpy(rk, key, keyLen);
for (int i = nk; i < 4 * (nr + 1); i++) {
uint8_t t[4];
int k = (i - 1) * 4;
t[0] = rk[k + 0]; t[1] = rk[k + 1]; t[2] = rk[k + 2]; t[3] = rk[k + 3];
if (i % nk == 0) {
uint8_t tmp = t[0];
t[0] = kSbox[t[1]]; t[1] = kSbox[t[2]];
t[2] = kSbox[t[3]]; t[3] = kSbox[tmp];
t[0] ^= kRcon[i / nk];
} else if (nk > 6 && i % nk == 4) {
t[0] = kSbox[t[0]]; t[1] = kSbox[t[1]];
t[2] = kSbox[t[2]]; t[3] = kSbox[t[3]];
}
int j = i * 4;
k = (i - nk) * 4;
rk[j + 0] = (uint8_t)(rk[k + 0] ^ t[0]);
rk[j + 1] = (uint8_t)(rk[k + 1] ^ t[1]);
rk[j + 2] = (uint8_t)(rk[k + 2] ^ t[2]);
rk[j + 3] = (uint8_t)(rk[k + 3] ^ t[3]);
}
return true;
}
// The state is column-major: s[4 * col + row], matching the AES input map.
static inline void AddRoundKey(uint8_t* s, const uint8_t* rk, int round) {
const uint8_t* k = rk + round * 16;
for (int i = 0; i < 16; i++) s[i] ^= k[i];
}
static void SubShift(uint8_t* s) {
for (int i = 0; i < 16; i++) s[i] = kSbox[s[i]];
uint8_t t;
// Row 1 left by one.
t = s[1]; s[1] = s[5]; s[5] = s[9]; s[9] = s[13]; s[13] = t;
// Row 2 left by two.
t = s[2]; s[2] = s[10]; s[10] = t;
t = s[6]; s[6] = s[14]; s[14] = t;
// Row 3 left by three (equivalently right by one).
t = s[15]; s[15] = s[11]; s[11] = s[7]; s[7] = s[3]; s[3] = t;
}
static void InvShiftSub(uint8_t* s) {
uint8_t t;
// Row 1 right by one.
t = s[13]; s[13] = s[9]; s[9] = s[5]; s[5] = s[1]; s[1] = t;
// Row 2 right by two.
t = s[2]; s[2] = s[10]; s[10] = t;
t = s[6]; s[6] = s[14]; s[14] = t;
// Row 3 right by three.
t = s[3]; s[3] = s[7]; s[7] = s[11]; s[11] = s[15]; s[15] = t;
for (int i = 0; i < 16; i++) s[i] = kRsbox[s[i]];
}
static void MixColumns(uint8_t* s) {
for (int c = 0; c < 4; c++) {
uint8_t* p = s + c * 4;
uint8_t a0 = p[0];
uint8_t all = (uint8_t)(p[0] ^ p[1] ^ p[2] ^ p[3]);
p[0] ^= (uint8_t)(Xtime((uint8_t)(p[0] ^ p[1])) ^ all);
p[1] ^= (uint8_t)(Xtime((uint8_t)(p[1] ^ p[2])) ^ all);
p[2] ^= (uint8_t)(Xtime((uint8_t)(p[2] ^ p[3])) ^ all);
p[3] ^= (uint8_t)(Xtime((uint8_t)(p[3] ^ a0)) ^ all);
}
}
static void InvMixColumns(uint8_t* s) {
for (int c = 0; c < 4; c++) {
uint8_t* p = s + c * 4;
uint8_t a = p[0], b = p[1], d = p[2], e = p[3];
p[0] = (uint8_t)(GfMul(a, 0x0e) ^ GfMul(b, 0x0b) ^ GfMul(d, 0x0d) ^ GfMul(e, 0x09));
p[1] = (uint8_t)(GfMul(a, 0x09) ^ GfMul(b, 0x0e) ^ GfMul(d, 0x0b) ^ GfMul(e, 0x0d));
p[2] = (uint8_t)(GfMul(a, 0x0d) ^ GfMul(b, 0x09) ^ GfMul(d, 0x0e) ^ GfMul(e, 0x0b));
p[3] = (uint8_t)(GfMul(a, 0x0b) ^ GfMul(b, 0x0d) ^ GfMul(d, 0x09) ^ GfMul(e, 0x0e));
}
}
void AesEncryptBlock(const AesCtx& ctx, const uint8_t in[16], uint8_t out[16]) {
uint8_t s[16];
memcpy(s, in, 16);
AddRoundKey(s, ctx.RoundKey, 0);
for (int round = 1; round < ctx.Rounds; round++) {
SubShift(s);
MixColumns(s);
AddRoundKey(s, ctx.RoundKey, round);
}
SubShift(s);
AddRoundKey(s, ctx.RoundKey, ctx.Rounds);
memcpy(out, s, 16);
}
void AesDecryptBlock(const AesCtx& ctx, const uint8_t in[16], uint8_t out[16]) {
uint8_t s[16];
memcpy(s, in, 16);
AddRoundKey(s, ctx.RoundKey, ctx.Rounds);
for (int round = ctx.Rounds - 1; round > 0; round--) {
InvShiftSub(s);
AddRoundKey(s, ctx.RoundKey, round);
InvMixColumns(s);
}
InvShiftSub(s);
AddRoundKey(s, ctx.RoundKey, 0);
memcpy(out, s, 16);
}
// =========================================================================
// RFC 3394 AES key wrap
// =========================================================================
static const uint8_t kKeyWrapIv[8] = {
0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6
};
bool AesKeyUnwrap(const uint8_t* kek, size_t kekLen,
const uint8_t* in, size_t inLen, uint8_t* out) {
if (inLen < 24 || (inLen % 8) != 0) return false;
size_t n = inLen / 8 - 1;
AesCtx ctx;
if (!AesInit(ctx, kek, kekLen)) return false;
uint8_t a[8];
memcpy(a, in, 8);
memcpy(out, in + 8, n * 8);
uint8_t block[16];
for (int j = 5; j >= 0; j--) {
for (size_t i = n; i >= 1; i--) {
uint64_t t = (uint64_t)n * (uint64_t)j + i;
memcpy(block, a, 8);
// A ^= t, big-endian over the full 8-byte word.
for (int b = 0; b < 8; b++)
block[7 - b] ^= (uint8_t)(t >> (8 * b));
memcpy(block + 8, out + (i - 1) * 8, 8);
AesDecryptBlock(ctx, block, block);
memcpy(a, block, 8);
memcpy(out + (i - 1) * 8, block + 8, 8);
}
}
SecureZero(block, sizeof(block));
SecureZero(&ctx, sizeof(ctx));
return SecureEqual(a, kKeyWrapIv, 8);
}
bool AesKeyWrap(const uint8_t* kek, size_t kekLen,
const uint8_t* in, size_t inLen, uint8_t* out) {
if (inLen < 16 || (inLen % 8) != 0) return false;
size_t n = inLen / 8;
AesCtx ctx;
if (!AesInit(ctx, kek, kekLen)) return false;
uint8_t a[8];
memcpy(a, kKeyWrapIv, 8);
memcpy(out + 8, in, inLen);
uint8_t block[16];
for (int j = 0; j < 6; j++) {
for (size_t i = 1; i <= n; i++) {
memcpy(block, a, 8);
memcpy(block + 8, out + i * 8, 8);
AesEncryptBlock(ctx, block, block);
uint64_t t = (uint64_t)n * (uint64_t)j + i;
memcpy(a, block, 8);
for (int b = 0; b < 8; b++)
a[7 - b] ^= (uint8_t)(t >> (8 * b));
memcpy(out + i * 8, block + 8, 8);
}
}
memcpy(out, a, 8);
SecureZero(block, sizeof(block));
SecureZero(&ctx, sizeof(ctx));
return true;
}
// =========================================================================
// AES-CMAC (RFC 4493)
// =========================================================================
static void CmacShiftLeft(const uint8_t in[16], uint8_t out[16]) {
uint8_t carry = 0;
for (int i = 15; i >= 0; i--) {
uint8_t v = in[i];
out[i] = (uint8_t)((v << 1) | carry);
carry = (uint8_t)((v >> 7) & 1);
}
}
void AesCmac(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[16]) {
AesCtx ctx;
if (!AesInit(ctx, key, keyLen)) {
memset(out, 0, 16);
return;
}
// Subkey generation.
uint8_t zero[16] = {};
uint8_t l[16], k1[16], k2[16];
AesEncryptBlock(ctx, zero, l);
CmacShiftLeft(l, k1);
if (l[0] & 0x80) k1[15] ^= 0x87;
CmacShiftLeft(k1, k2);
if (k1[0] & 0x80) k2[15] ^= 0x87;
size_t total = 0;
for (int i = 0; i < count; i++) total += lens[i];
uint8_t x[16] = {};
uint8_t block[16];
uint32_t fill = 0;
// Stream the parts through, holding back the final block so it can be
// padded and XORed with the right subkey.
size_t consumed = 0;
for (int i = 0; i < count; i++) {
const uint8_t* p = parts[i];
size_t n = lens[i];
while (n) {
uint32_t take = 16 - fill;
if (take > n) take = (uint32_t)n;
memcpy(block + fill, p, take);
fill += take;
p += take;
n -= take;
consumed += take;
if (fill == 16 && consumed < total) {
for (int b = 0; b < 16; b++) x[b] ^= block[b];
AesEncryptBlock(ctx, x, x);
fill = 0;
}
}
}
if (fill == 16 && total != 0) {
for (int b = 0; b < 16; b++) block[b] ^= k1[b];
} else {
block[fill] = 0x80;
for (uint32_t b = fill + 1; b < 16; b++) block[b] = 0;
for (int b = 0; b < 16; b++) block[b] ^= k2[b];
}
for (int b = 0; b < 16; b++) x[b] ^= block[b];
AesEncryptBlock(ctx, x, out);
SecureZero(l, sizeof(l));
SecureZero(k1, sizeof(k1));
SecureZero(k2, sizeof(k2));
SecureZero(block, sizeof(block));
SecureZero(&ctx, sizeof(ctx));
}
}
+132
View File
@@ -0,0 +1,132 @@
/*
* Crypto.hpp
* Minimal kernel-side crypto primitives.
*
* These exist for the Wi-Fi WPA2/WPA3 supplicant, which has to run inside the
* kernel (the 4-way handshake is bound to the driver's TX/RX path and has to
* complete before any IP traffic can flow). BearSSL lives in userspace and
* is not linkable here, so the handful of primitives the handshake needs are
* implemented directly:
*
* SHA-1 / SHA-256 digest + HMAC EAPOL MIC, PRF, PBKDF2
* PBKDF2-HMAC-SHA1 WPA passphrase -> 256-bit PSK
* AES-128/256 key unwrap, CMAC
* AES key unwrap (RFC 3394) encrypted EAPOL key data
* AES-CMAC key-descriptor version 3 MIC
*
* Nothing here is constant-time hardened beyond avoiding secret-dependent
* branches in the comparison helper; it is not a general-purpose crypto
* library and should not be used as one.
*
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <cstddef>
namespace Kt::Crypto {
// =========================================================================
// SHA-1
// =========================================================================
constexpr uint32_t SHA1_DIGEST_SIZE = 20;
constexpr uint32_t SHA1_BLOCK_SIZE = 64;
struct Sha1Ctx {
uint32_t State[5];
uint64_t Count; // total bytes fed
uint8_t Buffer[SHA1_BLOCK_SIZE];
uint32_t Partial;
};
void Sha1Init(Sha1Ctx& ctx);
void Sha1Update(Sha1Ctx& ctx, const void* data, size_t len);
void Sha1Final(Sha1Ctx& ctx, uint8_t out[SHA1_DIGEST_SIZE]);
void Sha1(const void* data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]);
// =========================================================================
// SHA-256
// =========================================================================
constexpr uint32_t SHA256_DIGEST_SIZE = 32;
constexpr uint32_t SHA256_BLOCK_SIZE = 64;
struct Sha256Ctx {
uint32_t State[8];
uint64_t Count;
uint8_t Buffer[SHA256_BLOCK_SIZE];
uint32_t Partial;
};
void Sha256Init(Sha256Ctx& ctx);
void Sha256Update(Sha256Ctx& ctx, const void* data, size_t len);
void Sha256Final(Sha256Ctx& ctx, uint8_t out[SHA256_DIGEST_SIZE]);
void Sha256(const void* data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]);
// =========================================================================
// HMAC
// =========================================================================
// Multi-part variants: `parts`/`lens` describe `count` chunks that are
// hashed as one message. The 802.11 PRF feeds four or five chunks per
// iteration, so this avoids staging a concatenation buffer every time.
void HmacSha1(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[SHA1_DIGEST_SIZE]);
void HmacSha1(const uint8_t* key, size_t keyLen,
const void* data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]);
void HmacSha256(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[SHA256_DIGEST_SIZE]);
void HmacSha256(const uint8_t* key, size_t keyLen,
const void* data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]);
// =========================================================================
// PBKDF2-HMAC-SHA1 (WPA passphrase -> PSK, RFC 2898)
// =========================================================================
void Pbkdf2Sha1(const char* password, size_t passLen,
const uint8_t* salt, size_t saltLen,
uint32_t iterations, uint8_t* out, size_t outLen);
// =========================================================================
// AES (128 and 256 bit keys, single block)
// =========================================================================
struct AesCtx {
uint8_t RoundKey[240]; // 15 round keys, the AES-256 maximum
int Rounds;
};
// keyLen must be 16 or 32 bytes. Returns false otherwise.
bool AesInit(AesCtx& ctx, const uint8_t* key, size_t keyLen);
void AesEncryptBlock(const AesCtx& ctx, const uint8_t in[16], uint8_t out[16]);
void AesDecryptBlock(const AesCtx& ctx, const uint8_t in[16], uint8_t out[16]);
// RFC 3394 AES key wrap / unwrap. `outLen` is `inLen - 8` for unwrap and
// `inLen + 8` for wrap; both operate on multiples of 8 bytes. Unwrap
// returns false when the integrity check value does not match.
bool AesKeyUnwrap(const uint8_t* kek, size_t kekLen,
const uint8_t* in, size_t inLen, uint8_t* out);
bool AesKeyWrap(const uint8_t* kek, size_t kekLen,
const uint8_t* in, size_t inLen, uint8_t* out);
// AES-CMAC (RFC 4493), truncated by the caller as needed.
void AesCmac(const uint8_t* key, size_t keyLen,
const uint8_t* const* parts, const size_t* lens, int count,
uint8_t out[16]);
// =========================================================================
// Helpers
// =========================================================================
// Length-fixed comparison that does not short-circuit on the first
// difference: used for MIC checks so a mismatch position is not observable.
bool SecureEqual(const void* a, const void* b, size_t len);
// Wipe key material. Marked so the compiler cannot elide the stores.
void SecureZero(void* p, size_t len);
}