{"record":{"id":"9ed06cbd37864333","repo":"beemdevelopment/Aegis","slug":"invalid-yandex-secret-length-d-bytes","errorCode":null,"errorMessage":"Invalid Yandex secret length: %d bytes","messagePattern":"Invalid Yandex secret length: (.+?) bytes","errorType":"validation","errorClass":"OtpInfoException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/otp/YandexInfo.java","lineNumber":112,"sourceCode":"\n    public static byte[] parseSecret(byte[] secret) throws OtpInfoException {\n        validateSecret(secret);\n\n        if (secret.length != SECRET_LENGTH) {\n            return Arrays.copyOfRange(secret, 0, SECRET_LENGTH);\n        }\n\n        return secret;\n    }\n\n    /**\n     * Java implementation of ChecksumIsValid\n     * From: https://github.com/norblik/KeeYaOtp/blob/188a1a99f13f82e4ef8df8a1b9b9351ba236e2a1/KeeYaOtp/Core/Secret.cs\n     * License: GPLv3+\n     */\n    public static void validateSecret(byte[] secret) throws OtpInfoException {\n        if (secret.length != SECRET_LENGTH && secret.length != SECRET_FULL_LENGTH) {\n            throw new OtpInfoException(String.format(\"Invalid Yandex secret length: %d bytes\", secret.length));\n        }\n\n        // Secrets originating from a QR code do not have a checksum, so we assume those are valid\n        if (secret.length == SECRET_LENGTH) {\n            return;\n        }\n\n        char originalChecksum = (char) ((secret[secret.length - 2] & 0x0F) << 8 | secret[secret.length - 1] & 0xff);\n\n        char accum = 0;\n        int accumBits = 0;\n\n        int inputTotalBitsAvailable = secret.length * 8 - 12;\n        int inputIndex = 0;\n        int inputBitsAvailable = 8;\n\n        while (inputTotalBitsAvailable > 0) {\n            int requiredBits = 13 - accumBits;","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/otp/YandexInfo.java#L94-L130","documentation":"YandexInfo.validateSecret requires the Yandex OTP secret to be exactly SECRET_LENGTH or SECRET_FULL_LENGTH bytes. QR-code secrets omit the checksum; the full form includes it. Any other length indicates corrupt or malformed secret data, so OtpInfoException is thrown.","triggerScenarios":"Calling YandexInfo.validateSecret(byte[]) (via parseSecret) with a secret byte array whose length matches neither constant — e.g. base32-decoding errors, truncated QR payload, wrong key parameter in a yandex:// or otpauth URI.","commonSituations":"Importing Yandex Key entries from QR codes that were cropped or re-encoded, base32 strings containing whitespace/padding mishandled, exporting secrets from other tools with different encodings (hex vs base32 confusion).","solutions":["Re-scan or regenerate the Yandex QR code so the secret is complete","Verify the secret's encoding (base32 vs raw bytes) and decode it correctly before passing to parseSecret/validateSecret","Strip whitespace/padding from the encoded secret and confirm the decoded byte length equals SECRET_LENGTH or SECRET_FULL_LENGTH","Catch OtpInfoException around parseSecret and reject the entry with a clear message"],"exampleFix":"// before\nbyte[] secret = Base32.decode(secretString); // length may be wrong\nYandexInfo.validateSecret(secret);\n// after\nbyte[] secret = Base32.decode(secretString.replaceAll(\"\\\\s\", \"\"));\nif (secret.length != YandexInfo.SECRET_LENGTH && secret.length != YandexInfo.SECRET_FULL_LENGTH) {\n    throw new IllegalArgumentException(\"Expected \" + YandexInfo.SECRET_LENGTH + \" or \"\n        + YandexInfo.SECRET_FULL_LENGTH + \" bytes, got \" + secret.length);\n}\nYandexInfo.validateSecret(secret);","handlingStrategy":"validation","validationCode":"byte[] secret = Base32.decode(secretString.trim());\nif (secret.length != YandexInfo.SECRET_LENGTH && secret.length != YandexInfo.SECRET_FULL_LENGTH) {\n    throw new IllegalArgumentException(\"Yandex secret must be \"\n        + YandexInfo.SECRET_LENGTH + \" or \" + YandexInfo.SECRET_FULL_LENGTH + \" bytes\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    YandexInfo.validateSecret(secret);\n} catch (OtpInfoException e) {\n    Toast.makeText(ctx, \"Corrupt Yandex secret: re-scan the QR code\", Toast.LENGTH_LONG).show();\n}","preventionTips":["Always decode base32 with whitespace stripped and correct padding","Confirm the byte length before calling parseSecret/validateSecret","Re-scan QR codes at original resolution; avoid screenshots/recompression","Do not confuse hex and base32 encodings when exporting from other tools"],"tags":["otp","yandex","secret","validation","android"],"backgroundTag":"invalid-argument-format","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"}