feat: wi-fi - join WPA2/WPA3-PSK networks and carry traffic like ethernet
This commit is contained in:
1 parent
a01e63c717
commit
bbe1df62fd
40 files changed
+5878
-272
No files matched your search
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user