gists/thps1/thps1.c
2018-10-11 16:45:38 +02:00

168 lines
3.8 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t u8;
typedef int32_t s32;
typedef uint32_t u32;
typedef int64_t s64;
typedef uint64_t u64;
static u64 prng_state = 1;
static u32 prng() {
prng_state = 3935559000370003845 * prng_state + 1;
return prng_state ^ (prng_state >> 32);
}
typedef struct {
u8 name[13];
u8 payload[11];
u32 residual;
} code_t;
void translate(u8 *name) {
// in-place translation of ascii to alphabetical indices.
// the input argument must be able to hold at least 13 characters.
// the resulting translation is no longer null-terminated (if the input even was).
int i;
for (i = 0; i < 13; i++) {
u8 c = name[i];
if (c == '\0') break;
if (c >= 'a' && c <= 'z') {
name[i] = c - 'a';
} else if (c >= 'A' && c <= 'Z') {
name[i] = c - 'A';
} else {
name[i] = 26;
}
}
// remaining space gets filled with a null-like character:
for (; i < 13; i++) {
name[i] = 26;
}
}
u32 multu_hi(u32 a, u32 b) {
// returns the upper 32-bits of a * b (unsigned).
return (u32)(((u64)a * (u64)b) >> 32);
}
s32 mult_lo(s32 a, s32 b) {
// returns the lower 32-bits of a * b (signed).
return (s32)(((s64)a * (s64)b) & 0xFFFFFFFF);
}
void decode(code_t *code) {
u32 r = 0;
u32 a3 = 0x98534637;
for (int i = 0; i < 11; i++) {
u32 a1 = code->name[i];
u32 temp = 0x10000 - (a3 & 0xFFFF);
u32 a0 = a1 + 20 + temp;
u32 v1 = a0 % 27;
code->payload[i] = v1;
r += a1 + mult_lo(r, 0x75344393) + 0x92438937;
a3 = mult_lo(a3 + v1, 0x34985343) + 0x64358931;
}
code->residual = r;
}
int validate(const code_t *code) {
u32 r = code->residual;
if (code->name[11] != r % 27) return 0;
r /= 27;
if (code->name[12] != r % 27) return 0;
return 1;
}
void print_code(const code_t *code) {
for (int i = 0; i < 13; i++) {
char c = code->name[i];
c = c == 26 ? ' ' : c + 'A';
printf("%c", c);
}
printf(" -> ");
for (int i = 0; i < 11; i++) {
char c = code->payload[i];
c = c == 26 ? ' ' : c + 'A';
printf("%c", c);
}
printf("\n");
fflush(stdout);
}
int validate_entry(const char *entry) {
code_t code = {0};
for (int i = 0; i < 13; i++) {
code.name[i] = entry[i];
if (entry[i] == '\0') break;
}
translate(code.name);
decode(&code);
int result = validate(&code);
if (result) print_code(&code);
// DEBUG:
//if (result) printf("%s\n", entry);
//for (i = 0; i < 13; i++) printf(" %02X", code.name[i]); printf("\n");
//for (i = 0; i < 11; i++) printf(" %02X", code.payload[i]); printf("\n");
return result;
}
int bruteforce(int count) {
/* with a fixed seed, this is useful for testing.
the first 10 results should be:
AQPIEOJSFIMJE
JHVBASITTCRQR
RXCUCODXHPAIS
GEXEPVFQBBWZN
EZRTHUSHOWHYG
LBSXAAOTMSUXV
WCDKPNSEWDKAF
NJVIYIYNRWZJQ
RVGLKPYDWYCIA
CEIQQHURYAJAV
*/
int found = 0;
code_t code = {0};
while (found < count) {
for (int j = 0; j < 13; j++) code.name[j] = prng() % 27;
//for (int j = 3; j < 13; j++) code.name[j] = 26;
decode(&code);
if (validate(&code)) {
found++;
print_code(&code);
}
}
return 1;
}
int main(int argc, char **argv) {
if (argc == 1) {
// self-test. only one code was known prior, so...
if (!validate_entry("TYR")) return 1;
return 0;
}
int invalid_count = 0;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "bruteforce")) {
invalid_count += !bruteforce(10);
} else {
invalid_count += !validate_entry(arg);
}
}
return !!invalid_count;
}