{"record":{"id":"68657a9e55759bdf","repo":"beemdevelopment/Aegis","slug":"invalid-number-of-iterations-for-pbkdf-d","errorCode":null,"errorMessage":"Invalid number of iterations for PBKDF: %d","messagePattern":"Invalid number of iterations for PBKDF: (.+?)","errorType":"exception","errorClass":"DatabaseImporterException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java","lineNumber":121,"sourceCode":"                int len = _data.length - offset - NONCE_SIZE - TAG_SIZE;\n                CryptResult result = CryptoUtils.decrypt(_data, offset + NONCE_SIZE, len, cipher, params);\n                return read(result.getData());\n            } catch (IOException | BadPaddingException | JSONException e) {\n                throw new DatabaseImporterException(e);\n            } catch (NoSuchAlgorithmException\n                    | InvalidAlgorithmParameterException\n                    | InvalidKeyException\n                    | NoSuchPaddingException\n                    | IllegalBlockSizeException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        private PBKDFTask.Params getKeyDerivationParams(char[] password) throws DatabaseImporterException {\n            byte[] iterBytes = Arrays.copyOfRange(_data, 0, INT_SIZE);\n            int iterations = ByteBuffer.wrap(iterBytes).getInt();\n            if (iterations < 1) {\n                throw new DatabaseImporterException(String.format(\"Invalid number of iterations for PBKDF: %d\", iterations));\n            }\n            // If number of iterations is this high, it's probably not an andOTP file, so\n            // abort early in order to prevent having to wait for an extremely long key derivation\n            // process, only to find out that the user picked the wrong file\n            if (iterations > 10_000_000L) {\n                throw new DatabaseImporterException(String.format(\"Unexpectedly high number of iterations: %d\", iterations));\n            }\n\n            byte[] salt = Arrays.copyOfRange(_data, INT_SIZE, INT_SIZE + SALT_SIZE);\n            return new PBKDFTask.Params(\"PBKDF2WithHmacSHA1\", KEY_SIZE, password, salt, iterations);\n        }\n\n        protected DecryptedState decryptOldFormat(char[] password) throws DatabaseImporterException {\n            // WARNING: DON'T DO THIS IN YOUR OWN CODE\n            // this exists solely to support the old andOTP backup format\n            // it is not a secure way to derive a key from a password\n            MessageDigest hash;\n            try {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java#L103-L139","documentation":"Aegis's andOTP importer reads the PBKDF2 iteration count from the first 4 bytes of the imported file. If the parsed integer is below 1, the file cannot contain a valid andOTP key derivation parameter set, so the importer aborts with a DatabaseImporterException before attempting an expensive PBKDF run.","triggerScenarios":"Importing a file into Aegis's andOTP importer whose first 4 bytes decode to an integer < 1 — e.g. the user selected a non-andOTP file (wrong export format, encrypted vault, image, etc.) whose leading bytes happen to parse as 0 or negative.","commonSituations":"User picks the wrong backup file in the Aegis import dialog; an andOTP export that was re-encoded, truncated, or corrupted; a file with a different header layout (other apps' exports) that shifts the iteration field.","solutions":["Verify the file is a genuine andOTP encrypted backup (starts with the iteration count as a big-endian int32)","Re-export from andOTP and select that file instead","Open the file in a hex editor and check the first 4 bytes decode to a plausible iteration count (> 0)","Use Aegis's other importers if the file came from a different authenticator app"],"exampleFix":"// before\ncursor.moveToFirst(); // wrong file opened as andOTP backup\nbyte[] iterBytes = Arrays.copyOfRange(_data, 0, INT_SIZE);\n// after\nif (_data.length < INT_SIZE) {\n    throw new DatabaseImporterException(\"File too small to be an andOTP backup\");\n}\nint iterations = ByteBuffer.wrap(_data, 0, INT_SIZE).getInt();\nif (iterations < 1) {\n    throw new DatabaseImporterException(\"Not an andOTP backup: invalid PBKDF iteration count\");\n}","handlingStrategy":"validation","validationCode":"byte[] head = Arrays.copyOfRange(fileBytes, 0, 4);\nint iterations = ByteBuffer.wrap(head).getInt();\nif (iterations < 1 || iterations > 10_000_000) {\n    throw new IllegalArgumentException(\"Not an andOTP backup: iterations=\" + iterations);\n}","typeGuard":null,"tryCatchPattern":"try {\n    importer.read(stream, password);\n} catch (DatabaseImporterException e) {\n    if (e.getMessage().contains(\"iterations\")) {\n        showUserError(\"Selected file is not a valid andOTP backup\");\n    }\n}","preventionTips":["Only feed andOTP's own encrypted export files to this importer","Check the file header (first 4 bytes) before import","Keep andOTP exports unmodified — don't re-zip or re-encode"],"tags":["import","pbkdf2","validation","android"],"backgroundTag":"invalid-argument-value","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}