---
title: 'Aegis APK Reverse Engineering: Login Codes — REA'
url: https://rea.tools/showcase/aegis/
---

[Home](https://rea.tools/) / [Showcases](https://rea.tools/showcase/) / Android

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](https://github.com/beemdevelopment/Aegis)

From an APK to a code you can reproduce

1. 01 · Locate the feature

   **3,077 classes → generating methods**

   REA searches the package and traces the code used by the display
   and copy actions.
2. 02 · Read the calculation

   **Time + account key → six digits**

   REA returns the methods and defaults. The agent explains how they
   fit together.
3. 03 · Rebuild and check

   **Change the clock. Watch the code.**

   A new browser demo reproduces the rule and checks it against
   public reference cases.

## 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 ↓](https://rea.tools/showcase/aegis/#try)

## 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. Time

   **59 seconds**

   Our adjustable example clock
2. 30-second block

   **Block 1**

   `floor(59 / 30)`  
   30–59 seconds
3. Generated code

   **287082**

   Account key + block → six digits

Example time, in seconds



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](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B). The demo implements the calculation recovered from the APK.



The diagram shows the result at 59 seconds. Enable JavaScript
to change the clock.

## 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. #### 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)`.

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.](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE)

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](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/HOTP.java).

## 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 time | Block | Six-digit code |
| --- | --- | --- |
| 30 seconds | 1 | `287082` |
| 59 seconds | 1 | `287082` |
| 60 seconds | 2 | `359152` |
| 1,111,111,109 seconds | 37,037,036 | `081804` |

The generator also matches the six published SHA-1 reference cases
in
[RFC 6238](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B). Check the browser implementation against them:

[Read the demo's calculation](https://rea.tools/assets/aegis-otp.js)

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](https://rea.tools/get-started/), then download these
two files into the same folder. On Windows x64, use a package with
matching Windows native controls and owned protocol stdin support;
see the
[Android provider guide](https://github.com/morluto/rea/blob/main/docs/android-analysis.md)
for prerequisites and PowerShell setup. A Linux environment such
as WSL is another option.

Android analysis needs a full JDK 17 or later. Select it with
`JAVA_HOME` or `PATH`. This case used JDK
21.

[Download Aegis APK](https://github.com/beemdevelopment/Aegis/releases/download/v3.4.3/aegis-v3.4.3.apk)[Download Android analysis provider](https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar)

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
```

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

- [Official Aegis 3.4.3 release](https://github.com/beemdevelopment/Aegis/releases/tag/v3.4.3)
  ·
  [Matching source commit](https://github.com/beemdevelopment/Aegis/tree/d6f4e5925a97e4e91593f1542085eae03432a759)
  ·
  [GPL-3.0 license](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE)
- [RFC 6238 reference cases](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B)
  · [Demo test data](https://rea.tools/showcase/aegis/test-vectors.json)

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.
