Android / Worked case study / 18 min read

Aegis: why does the code change at 60 seconds?

An authenticator looks like six digits and a timer. Follow the documented REA investigation of Aegis, then try a small version with a clock you control.

On this page
The goal

Recover one time-based code calculation, reproduce it in the browser, and check the boundary between two 30-second windows.

Before you start

The demo works immediately. To repeat the APK inspection, you need REA, Aegis v3.4.3, a full JDK 17+ and the jadx provider described in our Android guide.

01. Move from 59 to 60 seconds

Click the two time buttons below. With the fixed public test key, 59 seconds produces 287082; 60 seconds produces 359152. Move back to 30 seconds: the first code returns because 30 and 59 fall inside the same time window.

The 30-second code INTERACTIVE LAB

PUBLIC TEST KEY · SHA-1 · 6 DIGITS

287082

Unix time 59s Counter 1

0s30s60s90s120s

59 ÷ 30 → counter 1. At 60 seconds, the counter becomes 2.

New teaching implementation using RFC 6238’s public test key. No account secrets. No REA calls in your browser.

These numbers use seconds since the Unix epoch, not the seconds displayed on your wall clock. The demonstration implements standard TOTP with the public ASCII key 12345678901234567890. It does not read an Aegis vault or use an account secret.

02. Find the time-based method in the APK

The official REA report inspected Aegis v3.4.3 with REA 6.1.0. To follow its trail, obtain that release, record its hash and search for the TOTP class. Commands below use REA 6.4.0.

Terminal
npx -y rea-agents@6.4.0 inspect-android-package "/absolute/path/aegis-v3.4.3.apk" --json
npx -y rea-agents@6.4.0 search-android-classes "/absolute/path/aegis-v3.4.3.apk" Totp --json

Use the exact returned class name to inspect its members. The timestamp overload of getOtp is useful because you can supply the time directly. Select its overload from the inventory instead of assuming that the first method with that name is the right one.

Investigation prompt
Use REA to inspect the TotpInfo class in /absolute/path/aegis-v3.4.3.apk. Select the getOtp overload that accepts a timestamp. Follow its calls through counter encoding, HMAC, truncation and decimal formatting. Recover default period, hash and digit count. Cite each method inspected and distinguish defaults from per-account settings. Do not execute the APK.

03. Recover the whole calculation

Finding a division by 30 is only the start. The upstream report connects five stages: floor time into a counter; encode that counter in eight big-endian bytes; compute HMAC with the key; select a positive 31-bit value from the hash; reduce it to six digits and retain leading zeroes.

Time→Counter→HMAC→6 digits

Follow returned method calls rather than relying on familiar source names. In the inspected release, optimization placed generator helpers under kotlin.ExceptionsKt. The report also distinguishes default SHA-1, six digits and 30 seconds from account-specific settings.

04. Rebuild the rule and test the edges

Implement the recovered behavior independently, then use public reference vectors. Test just before and after a time boundary, a leading-zero result and both six- and eight-digit output. Adjacent windows are not mathematically guaranteed to have different codes, even though this example does.

Reconstruction prompt
Write a small independent TOTP implementation for this recovered behavior. Use only RFC 6238 public test keys. Verify all six SHA-1 reference timestamps at eight digits and their six-digit remainders. Test the 29/30 and 59/60 boundaries and preserve leading zeroes. Explain any difference from the inspected method; do not claim to rebuild the vault or Android app.
Unix timeCounter6-digit resultWhat it checks
290755224End of first window
301287082Boundary crossed
591287082Same window
602359152Next counter
111111110937037036081804Leading zero retained

05. Know what “133 checks” means

The upstream project reports agreement on 133 inputs between a JVM harness assembled from selected decompiled methods and independent reconstructions. It did not launch the original APK. That supports the selected calculation, not complete application equivalence.

For this site, we wrote a separate WebCrypto demonstration and checked the six published SHA-1 vectors at two digit lengths, plus every integer second from 0 to 120 against a separate Node HMAC implementation. We did not repeat the original APK investigation or its Java harness.

Sources: REA’s Aegis evidence report · RFC 6238 test vectors. Demo and explanation independently authored. No Aegis application source is redistributed here.

Next investigationHow does Notion copy rich text? →

New to the tool? What is REA? · How we check our guides