From 0270dd5739480acced62e045b5890ec0ebde2631 Mon Sep 17 00:00:00 2001 From: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:15:07 +0800 Subject: [PATCH] feat(website): add an Aegis APK code-generation showcase --- website/README.md | 18 +- website/evidence/aegis-login-code.md | 139 ++++ website/figures.md | 12 + website/public/assets/aegis-demo.js | 84 +++ website/public/assets/aegis-otp.js | 36 + website/public/assets/styles.css | 63 +- website/public/index.html | 10 +- website/public/showcase/aegis/index.html | 682 ++++++++++++++++++ .../public/showcase/aegis/test-vectors.json | 14 + website/public/showcase/index.html | 24 +- website/style-guide.md | 17 +- 11 files changed, 1084 insertions(+), 15 deletions(-) create mode 100644 website/evidence/aegis-login-code.md create mode 100644 website/public/assets/aegis-demo.js create mode 100644 website/public/assets/aegis-otp.js create mode 100644 website/public/showcase/aegis/index.html create mode 100644 website/public/showcase/aegis/test-vectors.json diff --git a/website/README.md b/website/README.md index 6f9af6887e..1b4d0806b0 100644 --- a/website/README.md +++ b/website/README.md @@ -4,7 +4,7 @@ > > — N0zoM1z0 -An English static website with explanatory figures, worked guides and DX-Ball, Notion, TH04 and CTF investigations. +An English static website with explanatory figures, worked guides and Aegis, DX-Ball, Notion, TH04 and CTF investigations. The public files are in `website/public/`. The site uses HTML, CSS and small scripts for copying code, following code comparisons and playing the dinosaur speed reconstruction. Python @@ -30,6 +30,7 @@ Open . Refresh the browser after editing a file. - `public/examples/dino-lab/index.html`: adjustable-speed mini-game, recovered rule, original-game check and browser analysis steps. - `public/first-investigation/index.html`: a guided Notes export investigation, from setup to a checked CSV prediction. - `public/showcase/index.html`: the case-study index. +- `public/showcase/aegis/index.html`: Aegis's Android login-code calculation, with an adjustable clock and reference checks. - `public/showcase/dx-ball/index.html`: sound-pan investigation and project status. - `public/showcase/notion/index.html`: Notion's Electron clipboard bridge and rich clipboard format. - `public/showcase/th04/index.html`: TH04's 16-bit DOS bullet-angle calculation and compiler checks. @@ -158,6 +159,21 @@ the website. [evidence/notion-clipboard.md](evidence/notion-clipboard.md) records the REA package version, selected findings, source anchors and module-probe scope. +## Aegis Android case study + +The Aegis case inspects the official 3.4.3 APK through REA 6.1.0. It follows +the default login-code calculation from class search and display references to +the time block, keyed hash and six-digit output. Selected decompiled Java is +paired with a readable summary; the original app and excerpts are credited to +Aegis under its GPL-3.0 license. + +`public/assets/aegis-otp.js` is a new teaching reconstruction using the fixed, +public RFC 6238 test key. `aegis-demo.js` connects it to the clock slider and +reference-check button. The diagram remains readable and controls stay disabled +without JavaScript. Keep the demo, selected code and +[evidence/aegis-login-code.md](evidence/aegis-login-code.md) aligned. The APK and +analysis-provider JAR remain official external downloads. + ## TH04 case study The TH04 case inspects the original PC-98 DOS angle helper through REA 4.1.0. diff --git a/website/evidence/aegis-login-code.md b/website/evidence/aegis-login-code.md new file mode 100644 index 0000000000..9567fba88a --- /dev/null +++ b/website/evidence/aegis-login-code.md @@ -0,0 +1,139 @@ +# Aegis Android login-code evidence + +Inspection on 9 October 2026, using the published REA 6.1.0 CLI with the +caller-supplied `jadx-headless-mcp` 0.7.1 provider and OpenJDK 21.0.12.1 on +Linux x64. The case follows the default time-based code in +[Aegis Authenticator](https://github.com/beemdevelopment/Aegis). + +## Target + +- [Official release](https://github.com/beemdevelopment/Aegis/releases/tag/v3.4.3): v3.4.3, published 6 September 2026. +- Artifact: `aegis-v3.4.3.apk`, 6,881,897 bytes. +- SHA-256: `fa091c91912c0d56ec3553394fac4158f9a9d178e93bcbb35419601d788cb528`. +- Package: `com.beemdevelopment.aegis`, version code 82. +- Reported inventory: 3,077 classes and 1,106 resources. +- Matching source commit: `d6f4e5925a97e4e91593f1542085eae03432a759`. +- Android provider JAR SHA-256: `6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`. +- Provider source revision: `5844800a486d2248fa949b74c9896285f5b54de2`. + +The artifact's SHA-256 matches the official GitHub release asset digest and every +saved REA record. The APK and provider JAR were downloaded into temporary storage. +They are linked from their official releases; neither file is a website asset. + +The CLI used `REA_JADX_MCP_JAR` for its own process. The connected MCP server's +Android provider was initially unavailable; it was not reconfigured. The page +expresses the inspected selections as REA tool requests, using the same operation +parameters as the successful CLI calls. Input paths and result package prefixes +are shortened for display, as noted beside those blocks. + +## REA findings + +Twenty REA operations completed successfully against this artifact. These are the +records used for the page's main findings: + +| Operation | Finding | Evidence ID | +| -------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------- | +| `inspect_android_method` | Display calls the timestamp overload for TotpInfo. | `ev_c4b51611c987a398359f526dc64866d97a99b146d0ec9177bc1d583a55d64bdc` | +| `trace_android_references` | TotpInfo is one of the three incoming generator callers. | `ev_6b5457705a67bd4952f7e17630bcf948538424fae5bc2951c08e921af7448d58` | +| `inspect_android_method` | Eight-byte big-endian counter, account key and HMAC. | `ev_cad4c03faf5b07347cba699d4f8f643c69e5a8f0c85f622fc8cbebe3f834f228` | +| `inspect_android_method` | Hash-byte selection produces a 31-bit integer. | `ev_5797488db16e72a4e9ad559a8f07e355adc1f73b9eb50566cfb46d47d1b6a883` | +| `inspect_android_method` | Algorithm name is prefixed with Hmac. | `ev_bffbfa3924c79823168df7daa01e8a3f62c4914d3fb4b54e5d4e60d21e75110f` | +| `trace_android_references` | Display and copy callers of the base method. | `ev_d90e3ed81b0bb6815826e4edf312eadec91b9d20ff06aaff61d38a191403676b` | +| `inspect_android_method` | Default SHA1 algorithm and six digits. | `ev_64294487704fa2a9877938017c95163e41c399ba958ea43c5b365118403ae0fe` | +| `inspect_android_method` | Decimal remainder and leading-zero padding. | `ev_5d0906d19ea9281fed36309794fd5697b54cac3abfbd17e2f78665e758e0eb60` | +| `inspect_android_method` | Class discriminator 0 selects the OTP formatting branch. | `ev_f0a82295bf6124055b7a12a21533950644267e8e8340038bca9c32db57d8d71f` | +| `inspect_android_package` | Package identity; 3,077 classes. | `ev_de07111c281cd76c4f2304f42448b06ef3199268815515d59caa02ea2480eb67` | +| `inspect_android_class` | Both getOtp overloads and their selection indices. | `ev_870577d702741f3023494c1bcb6567381a672a7132a60fd35ea2dd21ab9804c8` | +| `search_android_classes` | Three matches for the case-sensitive query Totp. | `ev_deeb75a09990e253881d0dc1932097abe00f94447d29a64c782fb48c42e9f492` | +| `inspect_android_method` | Current clock in milliseconds is converted to seconds. | `ev_ff4d8be5b4797e6f118d37ed55a2f65c9ceb8df39e8058d2cf36cd5561dc4bfd` | +| `inspect_android_method` | Default period: 30 seconds. | `ev_67af69cd39faceec616a9b1b48b03a5ea71b9c94ff2d1263a0a0063029cc1104` | +| `inspect_android_method` | Time is floored into a block before generation. | `ev_f282f536e5690e3e2253903bd979687687cfa5a9a4480758b4cd261da145caaf` | + +All selected methods returned complete Java representations without a fallback. +Signatures are provider display signatures; exact DEX descriptors, source-line +locations and instruction offsets are not available from these results. +Reference lists are static incoming references, with complete coverage of the +engine's reported set; reflection and dynamic loading remain unverified. + +## Interpretation + +`TotpInfo.getOtp()` obtains Unix time in seconds. Its timestamp overload uses +`floor(seconds / period)` as the generator's counter. The default constructor +sets the period to 30; the base constructor sets SHA1 and six digits. +These are defaults, not settings imposed on every imported account. + +`getAlgorithm()` constructs `HmacSHA1` for that default. The generator encodes the +counter as eight big-endian bytes, calculates the keyed hash, uses the low four +bits of the final hash byte as an offset, and selects a positive 31-bit integer. +The `OTP.toString()` branch for discriminator 0 reduces it modulo the requested +power of ten and prepends zeroes. The inspected generator passes discriminator 0 +into the constructor, connecting that branch to the generated code. + +The release exposes `generateOTP` and `getHash` under `kotlin.ExceptionsKt`. +Original-source naming alone would not identify that class in this APK. +The call returned from `TotpInfo.getOtp(long)` supplies the helper name; REA's +method inspection and reference tracing establish its calculation and callers. + +The base `OtpInfo.getOtp` reference list includes `EntryHolder.getOtp` and +`MainActivity.copyEntryCode`. Inspecting `EntryHolder.getOtp(int)` then shows that +the display calls the timestamp overload for a `TotpInfo` account, with an +optional period offset. The page uses the unshifted code calculation. + +## Reconstruction checks + +A temporary JVM harness assembled six method bodies directly from saved REA +results: the timestamp method, algorithm getter, hash method, generator, output +constructor and output formatter. Imports, enclosing classes, fields, exception +type and test driver were supplied for the harness. The original APK was not run. + +An independently authored Python calculation agreed with those Java methods on +133 inputs. The website's separately authored WebCrypto implementation then +agreed with all 133 recorded Java outputs. + +The cases include: + +- All six [RFC 6238 SHA-1 reference vectors](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B) at eight digits, plus the corresponding six-digit remainders. +- Every integer second from 0 through 120, checking four complete 30-second blocks and four boundary samples. +- A leading-zero result at Unix time 1,111,111,109: `081804`. + +The fixed key is the public ASCII test string `12345678901234567890`. +At 30 and 59 seconds the result is `287082`; at 60 seconds it is `359152`. +The diagram labels these as example clock inputs. A code is recomputed for each +block; the checked examples change at their boundaries. Adjacent codes need not +be universally distinct. + +The page's “Run reference checks” button tests the browser calculation against +all six reference inputs at both digit lengths. Slider updates use a revision +number so an earlier asynchronous calculation cannot overwrite a later input. +The reference JSON contains public test data, not a captured account key. + +## Source comparison and attribution + +After obtaining the calculation through REA, selected files from the matching +release source were read for corroboration: + +- [HOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/HOTP.java): counter encoding, HMAC and byte selection. +- [TOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/TOTP.java): time-to-counter transformation. +- [OTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java): decimal output and zero padding. + +These files confirm the interpretation. The public page's selected decompiled +Java comes from the APK evidence. It includes fragments from several methods and +line wrapping for display; the paired readable summary is an explanation. +The expanded generator panel retains its complete returned body, with formatting +changes. Aegis excerpts are credited under the upstream +[GPL-3.0 license](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE). +The diagram, explanatory summary, demo and its WebCrypto implementation are new +teaching material; the demo is not recovered original application source. + +## Scope and maintenance + +The claim is a static APK investigation and a checked reconstruction of its +default code calculation. It covers neither Android UI execution nor APK +signature verification, split APK handling, native libraries or vault storage. +The shell walkthrough was executed on Linux. The PowerShell recipe uses the +same CLI/environment inputs but was not executed on a Windows host. + +Raw REA results, provider scratch paths, Java harness classes and the downloaded +binaries remain outside the public website. The public page uses generic filenames +and the public test key. Update the release identity, selected excerpts, demo, +reference data and these notes together when moving to another Aegis version. diff --git a/website/figures.md b/website/figures.md index c0ab93d609..5cf50ce29f 100644 --- a/website/figures.md +++ b/website/figures.md @@ -73,6 +73,18 @@ are new teaching code. Check that the fallback is visible with JavaScript disabled and hidden when the playable canvas loads. Keep the speed settings, source excerpt, reconstruction and original-result table consistent. +## Aegis login-code figure + +The Aegis overview and adjustable-clock figure are semantic HTML. The clock +diagram shows seconds, the 30-second block and the resulting six-digit code, +using the public RFC 6238 test key. It retains a static 59-second example when +JavaScript is disabled. The three code selections connect each operation to the +APK's selected Java and an explanatory summary. + +[evidence/aegis-login-code.md](evidence/aegis-login-code.md) records the inspected +APK, methods, source attribution and reconstruction checks. Keep the block +formula, default settings, slider outputs and reference cases consistent. + ## Original image generation prompts ## Prompt 1 diff --git a/website/public/assets/aegis-demo.js b/website/public/assets/aegis-demo.js new file mode 100644 index 0000000000..74afa79b30 --- /dev/null +++ b/website/public/assets/aegis-demo.js @@ -0,0 +1,84 @@ +import { generateExampleCode } from "./aegis-otp.js"; + +async function initializeAegisDemo() { + const demo = document.querySelector("[data-aegis-demo]"); + if (demo === null) return; + const clock = demo.querySelector("input[type=range]"); + const controls = demo.querySelectorAll("input, button"); + const status = demo.querySelector("[data-aegis-status]"); + let revision = 0; + + const updateTime = async (seconds) => { + const currentRevision = ++revision; + try { + const code = await generateExampleCode(seconds); + if (currentRevision !== revision) return false; + const block = Math.floor(seconds / 30); + demo.querySelector("[data-aegis-seconds]").textContent = String(seconds); + demo.querySelector("[data-aegis-block]").textContent = String(block); + demo.querySelector("[data-aegis-division]").textContent = + `floor(${seconds} / 30)`; + demo.querySelector("[data-aegis-window]").textContent = + `${block * 30}–${block * 30 + 29} seconds`; + demo.querySelector("[data-aegis-code]").textContent = code; + clock.setAttribute( + "aria-valuetext", + `${seconds} seconds, time block ${block}`, + ); + status.textContent = ""; + return true; + } catch { + if (currentRevision === revision) { + status.textContent = + "Interactive calculation is unavailable in this browser. The example shows the code at 59 seconds."; + } + return false; + } + }; + + if (!(await updateTime(Number(clock.value)))) return; + controls.forEach((control) => { + control.disabled = false; + }); + clock.addEventListener("input", async () => { + await updateTime(Number(clock.value)); + }); + demo.querySelectorAll("[data-aegis-time]").forEach((button) => { + button.addEventListener("click", async () => { + clock.value = button.getAttribute("data-aegis-time"); + await updateTime(Number(clock.value)); + }); + }); + + const checkButton = document.querySelector("[data-aegis-check]"); + const checkResult = document.querySelector("[data-aegis-check-result]"); + checkButton.disabled = false; + checkButton.addEventListener("click", async () => { + checkButton.disabled = true; + checkResult.textContent = "Checking…"; + try { + const response = await fetch( + new URL("../showcase/aegis/test-vectors.json", import.meta.url), + ); + if (!response.ok) throw new Error("Reference data unavailable"); + const { vectors } = await response.json(); + let passed = 0; + for (const vector of vectors) { + for (const digits of [6, 8]) { + const code = await generateExampleCode(vector.seconds, digits); + if (code === vector.sha1.slice(-digits)) passed += 1; + } + } + checkResult.textContent = + `${passed} / ${vectors.length * 2} checks passed ` + + "(six reference cases, at six and eight digits)."; + } catch { + checkResult.textContent = + "Could not run the reference checks. Try again."; + } finally { + checkButton.disabled = false; + } + }); +} + +await initializeAegisDemo(); diff --git a/website/public/assets/aegis-otp.js b/website/public/assets/aegis-otp.js new file mode 100644 index 0000000000..0952a6070c --- /dev/null +++ b/website/public/assets/aegis-otp.js @@ -0,0 +1,36 @@ +// Teaching reconstruction of the calculation inspected in Aegis v3.4.3. +// The key is public RFC 6238 test data, not an account credential. +const testKey = new TextEncoder().encode("12345678901234567890"); +let importedKey; + +/** Generate a code with the public test key and a 30-second time step. */ +export async function generateExampleCode(seconds, digits = 6) { + if ( + !Number.isSafeInteger(seconds) || + seconds < 0 || + ![6, 8].includes(digits) + ) { + throw new RangeError( + "Use a nonnegative integer time and six or eight digits.", + ); + } + importedKey ??= crypto.subtle.importKey( + "raw", + testKey, + { name: "HMAC", hash: "SHA-1" }, + false, + ["sign"], + ); + const key = await importedKey; + const block = Math.floor(seconds / 30); + const counter = new ArrayBuffer(8); + new DataView(counter).setBigUint64(0, BigInt(block), false); + const hash = new Uint8Array(await crypto.subtle.sign("HMAC", key, counter)); + const offset = hash[hash.length - 1] & 15; + const number = + ((hash[offset] & 127) << 24) | + ((hash[offset + 1] & 255) << 16) | + ((hash[offset + 2] & 255) << 8) | + (hash[offset + 3] & 255); + return String(number % 10 ** digits).padStart(digits, "0"); +} diff --git a/website/public/assets/styles.css b/website/public/assets/styles.css index adf44815bf..ee2a3772dc 100644 --- a/website/public/assets/styles.css +++ b/website/public/assets/styles.css @@ -261,6 +261,14 @@ section[id] { background: #38424e; text-decoration: none; } +.button.secondary { + border-color: var(--line); + background: var(--page); + color: var(--ink); +} +.button.secondary:hover { + background: var(--soft); +} .text-link { font-size: 0.95rem; font-weight: 500; @@ -689,6 +697,59 @@ pre code { .case-figure { margin-bottom: 3.2rem; } +.aegis-step-selector { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +.aegis-step-selector button { + min-height: 44px; +} +.aegis-calculation .flow-node { + display: flex; + align-items: center; + justify-content: center; + gap: 0.35rem; + min-height: 3.5rem; + font-variant-numeric: tabular-nums; +} +.aegis-calculation .aegis-code { + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + font-size: 1.5rem; + letter-spacing: 0.06em; +} +.aegis-clock-controls { + margin-top: 1.7rem; +} +.aegis-clock-controls label { + display: block; + font-size: 0.875rem; + font-weight: 500; +} +.aegis-clock-controls input { + display: block; + width: 100%; + min-height: 44px; + margin: 0; + accent-color: var(--accent); +} +.aegis-clock-scale { + display: flex; + justify-content: space-between; + color: var(--muted); + font-size: 0.75rem; + font-variant-numeric: tabular-nums; +} +.aegis-clock-controls .actions { + gap: 0.7rem; + margin-top: 1.2rem; +} +.aegis-clock-controls .caption { + margin-bottom: 0; +} +.aegis-demo .button:disabled, +[data-aegis-check]:disabled { + opacity: 0.6; + cursor: default; +} .step-selector { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); @@ -1456,7 +1517,7 @@ pre code { .comparison-label { padding-bottom: 0.8rem; border-bottom: 1px solid var(--line); - font-size: 0.875rem; + font-size: 1.125rem; font-weight: 600; } .agent-investigation > .comparison-label { diff --git a/website/public/index.html b/website/public/index.html index c8c3649add..492d595b41 100644 --- a/website/public/index.html +++ b/website/public/index.html @@ -167,7 +167,10 @@ class="manual-investigation step-comparison" data-step-comparison > -

Before REA · You investigate

+

+ Before REA · You + investigate +

-

With REA · You ask your agent

+

+ With REA · You ask + your agent +

One prompt.

Ask your agent in plain English. diff --git a/website/public/showcase/aegis/index.html b/website/public/showcase/aegis/index.html new file mode 100644 index 0000000000..d9bde85ac0 --- /dev/null +++ b/website/public/showcase/aegis/index.html @@ -0,0 +1,682 @@ + + + + + + How an authenticator generates a login code — REA + + + + + + + + + +

+
+
+ +

Case study · Aegis Authenticator

+

How does an authenticator
generate a login code?

+

+ A six-digit code changes every 30 seconds. Use REA to find the + calculation in an Android APK, then rebuild it with a clock you can + control. +

+

+ Aegis 3.4.3 · Android · Official release APK · 6.9 MB +

+ View Aegis on GitHub +
+ +
+

From an APK to a code you can reproduce

+
    +
  1. + 01 · Locate the feature3,077 classes → generating methodsREA searches the package and traces the code used by the display + and copy actions. +
  2. +
  3. + 02 · Read the calculationTime + account key → six digitsREA returns the methods and defaults. The agent explains how they + fit together. +
  4. +
  5. + 03 · Rebuild and checkChange the clock. Watch the code.A new browser demo reproduces the rule and checks it against + public reference cases. +
  6. +
+
+ +
+ +
+
+

Ask your agent

+

+ The app shows a changing login code. We want to find + what makes it change and reproduce that behavior. +

+
+
+ Your coding agent +
+

+ Use REA to inspect aegis-v3.4.3.apk. Find how it generates + the six-digit login code, explain why it changes every 30 + seconds, and rebuild a small demo where I can change the + time. +

+
+

+ An example prompt for the downloaded APK, with REA's Android + analysis configured. + Get the APK and analysis setup ↓ +

+
+ +
+

Try the code generator

+

+ The app groups time into + 30-second blocks. It combines + the block number with the account key to generate a code. The same + key and block produce the same code. +

+
+

Move the clock across 60 seconds

+
    +
  1. + Time59 secondsOur adjustable example clock +
  2. +
  3. + 30-second blockBlock 1floor(59 / 30)
    30–59 seconds
    +
  4. +
  5. + Generated code287082Account key + block → six digits +
  6. +
+
+ + + +
+ +
+

+ At 59 seconds: 287082. At 60 seconds: + 359152. Moving within one block leaves the + code unchanged. +

+

+
+
+ The key is fixed, public + RFC 6238 test data. The demo implements the calculation recovered from the APK. +
+ +
+
+ +
+

Find the code with REA

+
    +
  1. +

    Narrow the APK to three classes

    +
    +
    +
    Agent → REA
    +
    inspect_android_package
    +{"path": "aegis-v3.4.3.apk"}
    +
    +search_android_classes
    +{"path": "aegis-v3.4.3.apk",
    + "query": "Totp"}
    +
    +
    +
    + REA → agent · selected results +
    +
    Package: com.beemdevelopment.aegis
    +Version: 3.4.3
    +Classes: 3,077
    +
    +Three matches:
    +  otp.TotpInfo
    +  ui.views.TotpProgressBar
    +  importers.TotpAuthenticatorImporter
    +
    +
    +

    + TotpInfo is the calculation candidate; the other + two concern the progress bar and imported accounts. TOTP means + a time-based one-time password. +

    +
  2. +
  3. +

    Connect the generator to the app's display

    +
    +
    +
    Agent → REA
    +
    inspect_android_class
    +{"path": "aegis-v3.4.3.apk",
    + "class_name":
    +  "com.beemdevelopment.aegis.otp.TotpInfo"}
    +
    +trace_android_references
    +{"path": "aegis-v3.4.3.apk",
    + "class_name":
    +  "com.beemdevelopment.aegis.otp.OtpInfo",
    + "method_name": "getOtp"}
    +
    +
    +
    + REA → agent · methods and callers +
    +
    TotpInfo methods:
    +  getOtp()      · overload 0
    +  getOtp(long)  · overload 1
    +
    +OtpInfo.getOtp callers:
    +  ui.views.EntryHolder.getOtp
    +  ui.MainActivity.copyEntryCode
    +
    +
    +

    + REA identifies the timestamp overload and finds the base + method's callers. Inspecting + EntryHolder.getOtp then shows the display calling + TotpInfo.getOtp(timestamp). +

    +
  4. +
+

+ Requests show a shortened input path. Result class names omit the + common com.beemdevelopment.aegis prefix. +

+
+ +
+

Read the calculation

+

+ REA returns + the clock conversion, default settings and generating + methods. + The constructors set a 30-second interval and six digits; the + timestamp method turns seconds into a block number. +

+
+
+ + + +
+
+
+
+ REA · selected decompiled Java +
+
// TotpInfo defaults and clock
+setPeriod(30);
+getOtp(System.currentTimeMillis() / 1000);
+
+// OtpInfo default algorithm and digits
+this(bArr, "SHA1", 6);
+
+// Block passed to the generator
+(long) Math.floor(j / ((double) this._period))
+
+// generateOTP → getHash
+byte[] hash = getHash(bArr, str, j);
+
+// getHash: account key + encoded block
+Mac mac = Mac.getInstance(str);
+mac.init(secretKeySpec);
+return mac.doFinal(bArrArray);
+
+// OTP.toString, case 0
+this._code % ((int) Math.pow(10.0d, i))
+
+// Keep leading zeroes
+while (sb.length() < i) {
+    sb.insert(0, "0");
+}
+
+
+
+ Readable summary · agent's interpretation +
+
// Group time into 30-second blocks
+seconds = current_time_ms / 1000
+block = floor(seconds / 30)
+
+// Generate a number from the key and block
+hash = HMAC_SHA1(account_key,
+                 big_endian_8_bytes(block))
+number = select_31_bits(hash)
+
+// Format the default six-digit code
+code = number % 1_000_000
+code = pad_with_zeroes(code, 6)
+
+
+
+

+ Time chooses the block. Seconds 30 through 59 + all give block 1. At 60 seconds, the block becomes 2. +

+
+
+

+ The key matters too. The generator combines + the account key with the block. An account with a different + key can show a different code at the same time. +

+
+
+

+ Keep six digits. The remainder after division + by 1,000,000 is padded with zeroes when needed, so a code can + begin with 0. +

+
+
+

+ Excerpts from several inspected methods, with line wrapping for + display. The right panel is an explanatory summary. + Aegis code is GPL-3.0. +

+
+ Inside the generator +

+ The released APK puts generateOTP in + kotlin.ExceptionsKt. REA follows the call from + TotpInfo and returns this body; the original source + calls the helper HOTP. +

+
+
+ REA · generateOTP, complete body +
+
public static OTP generateOTP(byte[] bArr, String str,
+        int i, long j)
+        throws NoSuchAlgorithmException, InvalidKeyException {
+    byte[] hash = getHash(bArr, str, j);
+    int i2 = hash[hash.length - 1] & 15;
+    return new OTP(
+        (hash[i2 + 3] & 255)
+        | ((hash[i2] & 127) << 24)
+        | ((hash[i2 + 1] & 255) << 16)
+        | ((hash[i2 + 2] & 255) << 8), i, 0);
+}
+
+

+ The last hash byte selects a position. Four bytes at that + position provide a positive 31-bit number; formatting reduces it + to the requested number of digits. The constructor's last + argument selects the code-formatting branch introduced by + compilation. +

+

+ Compare with the matching release source. +

+
+
+ +
+

Check the reconstruction

+

+ We assembled the selected REA-returned Java methods in a small + test program. Their output matched an independent reconstruction + on 133 test cases, including time-block + boundaries and codes beginning with zero. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Example timeBlockSix-digit code
30 seconds1287082
59 seconds1287082
60 seconds2359152
1,111,111,109 seconds37,037,036081804
+
+

+ The generator also matches the six published SHA-1 reference cases + in + RFC 6238. Check the browser implementation against them: +

+
+ Read the demo's calculation +
+

+ The reference cases use eight digits. The check also verifies + their six-digit results. +

+

+ Verification runs extracted Java methods and the new demo. The + Android APK was inspected statically. +

+
+ +
+

Inspect the APK yourself

+

+ Set up REA, then download these + two files into the same folder. Android analysis needs Java; this + case was checked with Java 21. +

+ +

Point REA at the provider file and inspect the APK:

+
+
+ macOS / Linux +
+
REA_JADX_MCP_JAR="$PWD/jadx-headless-mcp-0.7.1-all.jar" \
+npx rea-agents@latest inspect-android-package \
+  ./aegis-v3.4.3.apk --format json --full-output
+
+
+ Windows PowerShell +
+
+ PowerShell +
+
$env:REA_JADX_MCP_JAR = (Resolve-Path ./jadx-headless-mcp-0.7.1-all.jar).Path
+npx rea-agents@latest inspect-android-package ./aegis-v3.4.3.apk --format json --full-output
+
+
+

+ Your agent can continue through REA's CLI using the prompt above. + For MCP, add REA_JADX_MCP_JAR to the REA server's + environment and reconnect it. +

+

+ A next question: + what happens when an account uses eight digits or a different + time interval? +

+
+ +
+

Sources

+ +

+ Inspected with REA 6.1.0 on 9 October 2026. Decompiled excerpts + are credited to Aegis; the diagram, readable summary and browser + reconstruction are new teaching material. +

+
+
+
+
+ +

+ Top + + diff --git a/website/public/showcase/aegis/test-vectors.json b/website/public/showcase/aegis/test-vectors.json new file mode 100644 index 0000000000..4807a2c30f --- /dev/null +++ b/website/public/showcase/aegis/test-vectors.json @@ -0,0 +1,14 @@ +{ + "source": "https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B", + "public_test_key_ascii": "12345678901234567890", + "algorithm": "HMAC-SHA1", + "period_seconds": 30, + "vectors": [ + { "seconds": 59, "sha1": "94287082" }, + { "seconds": 1111111109, "sha1": "07081804" }, + { "seconds": 1111111111, "sha1": "14050471" }, + { "seconds": 1234567890, "sha1": "89005924" }, + { "seconds": 2000000000, "sha1": "69279037" }, + { "seconds": 20000000000, "sha1": "65353130" } + ] +} diff --git a/website/public/showcase/index.html b/website/public/showcase/index.html index 4f30835639..146efd0e8b 100644 --- a/website/public/showcase/index.html +++ b/website/public/showcase/index.html @@ -6,7 +6,7 @@ Case studies — REA @@ -54,11 +54,29 @@

REA case studies.

- Game reconstruction, Electron analysis and a CTF challenge, with code, - figures and checked results. + Android, games, Electron and a CTF challenge, with code, figures and + checked results.

+
+

Android · APK analysis

+
+

How an authenticator generates a login code

+

+ Find Aegis's six-digit code calculation in its released APK. Trace + the generating methods with REA, then move a demo clock to see why + the code changes every 30 seconds. +

+
+ APK + Generating code + Recovered rule + Adjustable clock +
+ Read the Android case study +
+

CTF · Native analysis

diff --git a/website/style-guide.md b/website/style-guide.md index 285ba39ae6..a696837957 100644 --- a/website/style-guide.md +++ b/website/style-guide.md @@ -142,14 +142,15 @@ purposes, and immutable commit links for checkpoint-dependent facts. Each figure should answer a question that a reader can name. Choose the visual form that fits it: -| Relationship | Existing example | Useful form | -| ----------------------------------------- | ---------------------------------------- | ------------------------------------------------------- | -| Analysis and verification steps | DX-Ball sound pan | Flow with separate verification branches | -| Code across process boundaries | Electron CSV export and Notion clipboard | HTML flow with APIs and channel names | -| A mathematical transformation | TH04's fixed and aimed rings | SVG drawn from the angle formula | -| Original instructions and readable source | DX-Ball and TH04 | Selectable HTML code with matching step highlights | -| A request caused by an action | Notes browser export | Flow, observed result table and script excerpt | -| Recovered behavior and a new experiment | Dinosaur speed reconstruction | Inspected code, playable demo and original-result check | +| Relationship | Existing example | Useful form | +| ----------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------- | +| Analysis and verification steps | DX-Ball sound pan | Flow with separate verification branches | +| Code across process boundaries | Electron CSV export and Notion clipboard | HTML flow with APIs and channel names | +| A mathematical transformation | TH04's fixed and aimed rings | SVG drawn from the angle formula | +| Original instructions and readable source | DX-Ball and TH04 | Selectable HTML code with matching step highlights | +| A request caused by an action | Notes browser export | Flow, observed result table and script excerpt | +| Recovered behavior and a new experiment | Dinosaur speed reconstruction | Inspected code, playable demo and original-result check | +| Time blocks and changing output | Aegis login-code reconstruction | Adjustable clock, block number and code beside recovered methods | Use SVG or semantic HTML for diagrams with exact text, numbers and arrows. Generated illustrations can help explore a layout, but the published labels,