beemdevelopment/Aegis · error · DatabaseImporterException
Unexpected cipher
Error message
Unexpected cipher: %s
What it means
Each FreeOTP token entry embeds its own encrypted key JSON with an "mCipher" field. The importer only supports AES/GCM/NoPadding for per-entry keys; any other value aborts conversion of that entry with DatabaseImporterException (wrapped into a DatabaseImporterEntryException by convertEntry's caller vaultEntry).
Solutions
- Update Aegis; if unsupported, re-export from stock FreeOTP
- Inspect the entry's key JSON mCipher value to diagnose
- Re-save the token in stock FreeOTP so it is encrypted with GCM and re-export
- Delete/re-add the affected entry in FreeOTP if only some entries fail
Example fix
// before
if (!cipherName.equals("AES/GCM/NoPadding")) {
throw new DatabaseImporterException(String.format("Unexpected cipher: %s", cipherName));
}
// after
if (!cipherName.equals("AES/GCM/NoPadding")) {
throw new DatabaseImporterException(String.format(
"Unexpected cipher: %s for token key (only AES/GCM/NoPadding supported)", cipherName));
} Defensive patterns
Strategy: try-catch
Validate before calling
JSONObject keyObj = new JSONObject(encObj.getString("key"));
if (!keyObj.getString("mCipher").equals("AES/GCM/NoPadding")) {
// skip or warn about this entry
} Try / catch
try {
entry = importer.convertEntry(encObj, tokenObj);
} catch (DatabaseImporterEntryException e) {
log.warn("Entry skipped: " + e.getMessage());
} Prevention
- Scan all token entries' key JSON for mCipher before bulk import
- Re-save failing tokens in stock FreeOTP
- Keep backups unedited
When it happens
Trigger: Importing a FreeOTP backup where a token's key object has mCipher different from "AES/GCM/NoPadding" — modified FreeOTP build, schema drift, or hand-edited backup JSON.
Common situations: FreeOTP fork using CBC or another mode; partial manual edits to a backup; importing backups generated by tooling that reimplemented FreeOTP encryption differently.
Related errors
- Unexpected master key cipher
- Unexpected master key KDF
- unsupported otp type:
- Password incorrect
- Invalid number of iterations for PBKDF
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/e3c73ab5d43d0132.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java:245
VaultEntry vaultEntry = convertEntry(encObj, tokenObj);
result.addEntry(vaultEntry);
} catch (DatabaseImporterEntryException e) {
result.addError(e);
} catch (JSONException ignored) {
}
}
return result;
}
private VaultEntry convertEntry(JSONObject encObj, JSONObject tokenObj)
throws DatabaseImporterEntryException {
try {
JSONObject keyObj = new JSONObject(encObj.getString("key"));
String cipherName = keyObj.getString("mCipher");
if (!cipherName.equals("AES/GCM/NoPadding")) {
throw new DatabaseImporterException(String.format("Unexpected cipher: %s", cipherName));
}
byte[] cipherText = toBytes(keyObj.getJSONArray("mCipherText"));
byte[] parameters = toBytes(keyObj.getJSONArray("mParameters"));
byte[] token = keyObj.getString("mToken").getBytes(StandardCharsets.UTF_8);
byte[] nonce = parseNonce(parameters);
IvParameterSpec spec = new IvParameterSpec(nonce);
Cipher cipher = Cipher.getInstance(cipherName);
cipher.init(Cipher.DECRYPT_MODE, _masterKey, spec);
cipher.updateAAD(token);
byte[] secretBytes = cipher.doFinal(cipherText);
JSONArray secretArray = new JSONArray();
for (byte b : secretBytes) {
secretArray.put(b);
}
tokenObj.put("secret", secretArray);
View on GitHub (pinned to d6f4e5925a)