Frank Stevenson’s Kraken (github.com/0xh4di/kraken) builds on Apple Silicon after three patches and a semaphore shim. CPU cracker only. GPU (ATI CAL) not built.
Repo with patches and reproduction steps: github.com/AndreiGosman/kraken-macos-arm64. Kraken source not redistributed; clone upstream separately.
Base: 0xh4di/kraken, HEAD 368a837. That mirror already carries Frank’s two December 2010 Intel Mac fixes (stropts.h guard on __APPLE__, #define mmap64 mmap, open() simplification). Those help. They do not cover ARM64 or 2020+ toolchains.
Environment
- MacBook Pro M4, macOS 25.6.0 (Darwin 25.5 arm64)
- Apple clang 21, Xcode CLT
- Prefix
$HOME/sdr-lab/local, nothing in/opt/homebrew
Audit before patching
Portability greps on the tree. Results:
- SSE intrinsics: none.
grep -rn '_mm_\|__m128\|__m256\|__asm__' .→ empty.a5_cpu/is scalar C++ with lookup tables. - x86 inline assembly: none.
- ATI GPU: isolated via
dlopen.A5CpuStubs.cppandA5AtiStubs.cppload.sofiles at runtime. Skip buildingA5Ati.so, GPU disabled by absence. - Raw SATA NCQ ioctls (
SG_IO,BLKGETSIZE,HDIO_*): present inNcqDevice.cpp, not reached during build ora5cpu_test. Left alone. mmap64,open64,off64_t: onlymmap64referenced, already aliased tommapby Frank’s 2010 fix.stropts.h(SysV STREAMS, absent on macOS): guarded on__APPLE__by Frank’s 2010 fix.
Presumed blockers (SSE, GPU, NCQ) not applicable. Actual blockers below.
Patches
Applied in order from a fresh clone of 0xh4di/kraken.
001 semaphore shim. macOS sem_init on unnamed semaphores returns ENOSYS at runtime. Kraken uses sem_t in four headers (Kraken.h, ServerCore.h, NcqDevice.h, a5_cpu/A5Cpu.h) as binary mutexes (init value 1). Patch replaces #include <semaphore.h> with compat/darwin_sem_compat.h, which maps sem_t onto GCD dispatch semaphores on __APPLE__.
002 POSIX headers + bind(). Modern libc++ does not pull <unistd.h> transitively. sleep, usleep, close, read, write undeclared in a5cpu_test.cpp, Kraken.cpp, ServerCore.cpp. Added explicit #include <unistd.h> to each. ServerCore.cpp has using namespace std; at the top; unqualified bind() in socket setup resolved to std::bind from <functional>. Qualified as ::bind(...).
003 mincore signature. Darwin’s mincore() takes char * for the vector argument. Kraken passes unsigned char[4]. Added (char *) cast at the call site in NcqDevice.cpp.
Sizes: 2879 / 1269 / 533 bytes. All git-format, applicable with git apply.
Semaphore shim
#ifndef DARWIN_SEM_COMPAT_H
#define DARWIN_SEM_COMPAT_H
#if defined(__APPLE__)
#include <dispatch/dispatch.h>
typedef dispatch_semaphore_t sem_t;
static inline int sem_init(sem_t *s, int shared, unsigned val) {
(void)shared;
*s = dispatch_semaphore_create((long)val);
return *s ? 0 : -1;
}
static inline int sem_wait(sem_t *s) {
dispatch_semaphore_wait(*s, DISPATCH_TIME_FOREVER);
return 0;
}
static inline int sem_post(sem_t *s) {
dispatch_semaphore_signal(*s);
return 0;
}
static inline int sem_destroy(sem_t *s) {
dispatch_release(*s);
return 0;
}
#else
#include <semaphore.h>
#endif
#endif
GCD dispatch semaphores match POSIX counting-semaphore semantics for the use Kraken makes (init value 1, wait/post pairs across threads). Not equivalent for every POSIX case: no sem_timedwait, no sem_getvalue, no inter-process sharing. Kraken does not use those.
Validated at runtime by upstream a5cpu_test: 1000 chains, 8 worker threads, no deadlock, exit 0.
Build
git clone https://github.com/0xh4di/kraken.git
cd kraken
git apply ../kraken-macos-arm64/patches/001-darwin-semaphore-shim.patch
git apply ../kraken-macos-arm64/patches/002-posix-headers-and-bind.patch
git apply ../kraken-macos-arm64/patches/003-mincore-char-cast.patch
mkdir -p compat
cp ../kraken-macos-arm64/compat/darwin_sem_compat.h compat/
cd a5_cpu && ./build.sh # -> A5Cpu.so, a5cpu_test
cd ../Kraken && ./build.sh # -> kraken (links A5Cpu.so via stub)
Zero errors on Apple clang 21. Two warnings, cosmetic.
Binaries:
Kraken/kraken(main tool, needs rainbow tables at runtime)Kraken/A5Cpu.so(CPU cracker plugin)a5_cpu/a5cpu_test(chain self-test)
Cross-check against libosmocore
Independent validation of the A5/1 kernel before trusting Kraken with real tables. Approach: pure-Python A5/1 as intermediate oracle. Chain of trust:
- Python A5/1 = libosmocore (validated byte-for-byte against
libosmocore/tests/a5/a5_test.cvectors: Kc=0x0123456789ABCDEF, fn=123456; Kc=0xFFFFFFFFFFFFFFFF, fn=1000000). - Python A5/1 = Kraken kernel (validated below).
- Therefore Kraken kernel = libosmocore, transitively.
Harness in tools/xcheck/:
kraken_forwards.cpp: minimal driver overBidirectional::Forwards(state, nbits, out)fromUtilities/. Reads packed 64-bit state (R1 | R2<<19 | R3<<41) on argv, prints keystream as hex.xcheck_a5cpu_vs_osmocom.py: initializes Python A5/1 with (Kc, fn), extracts registers, packs Kraken-style, generates two Python reference streams (clock-first and output-first), calls the C++ helper, compares.
Result on both test vectors: kernel match, byte-for-byte, output-first convention. Log at logs/a5_1_xcheck-python-vs-kraken-20260906.log.
Bit offset between osmocom and Kraken
First cross-check run failed by one bit. The two streams were not random-different, they were offset:
osmocom: 1 1 0 0 1 0 1 1 1 0 1 0 0 0 1 0 ...
Kraken: 1 1 1 0 0 1 0 1 1 1 0 1 0 0 0 1 ...
^ Kraken emits one extra leading bit
osmocom[i] == Kraken[i+1] for i >= 0.
Cause is the loop layout:
- libosmocore: clock LFSRs, then read output tap.
- Kraken: read output tap, then clock LFSRs.
Same registers, feedback taps, majority rule, output taps. Reverse order per iteration.
Neither implementation is wrong. Frank’s Bidirectional.cpp does not comment on it. libosmocore’s a5.c does not either. 3GPP TS 55.205 specifies the algorithm, not the loop.
Interface consequence: if you feed Kraken raw keystream extracted with gr-gsm (osmocom-based), skip the first Kraken output bit or prepend a padding bit on the osmocom side. Kraken’s rainbow tables and internal chain math are self-consistent in Kraken’s convention.
License notes
Kraken headers: Copyright 2009. Frank A. Stevenson. All rights reserved. Permission to distribute, modify and copy is granted to the TMTO project. Ambiguous for third-party redistribution.
My repo ships patches and helper tooling only. No Kraken source is redistributed. Users clone 0xh4di/kraken themselves and apply the patches locally. New files (compat header, cross-check harness, Python A5/1) are MIT.
Legal note (RO)
A5/1 crack empirically requires a keystream capture. In Romania, capturing anything other than your own SIM/traffic falls under Cod Penal art. 226 and Legea 51/1991. ANCOM regulates unlicensed emission. Rainbow-table decryption itself is a math operation.