{"record":{"id":"fb1e75c096a36bff","repo":"gchq/CyberChef","slug":"invalid-secret-the-input-must-be-a-valid-base32-s-fb1e75","errorCode":null,"errorMessage":"Invalid secret. The input must be a valid base32 string (characters A–Z and 2–7).","messagePattern":"Invalid secret\\. The input must be a valid base32 string \\(characters A–Z and 2–7\\)\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/GenerateTOTP.mjs","lineNumber":70,"sourceCode":"                \"min\": 1,\n                \"integer\": true\n            }\n        ];\n    }\n\n    /**\n     *\n     */\n    run(input, args) {\n        const secretStr = new TextDecoder(\"utf-8\").decode(input).trim();\n\n        let secret;\n        try {\n            secret = secretStr ?\n                OTPAuth.Secret.fromBase32(secretStr.toUpperCase().replace(/\\s+/g, \"\")) :\n                new OTPAuth.Secret();\n        } catch {\n            throw new OperationError(\"Invalid secret. The input must be a valid base32 string (characters A–Z and 2–7).\");\n        }\n\n        const totp = new OTPAuth.TOTP({\n            issuer: \"\",\n            label: args[0],\n            algorithm: \"SHA1\",\n            digits: args[1],\n            period: args[3],\n            epoch: args[2] * 1000, // Convert seconds to milliseconds\n            secret\n        });\n\n        const uri = totp.toString();\n        const code = totp.generate();\n\n        return `URI: ${uri}\\n\\nPassword: ${code}`;\n    }\n}","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/GenerateTOTP.mjs#L52-L88","documentation":"Thrown by GenerateTOTP when the user-supplied secret cannot be parsed as a valid RFC 4648 base32 string. Identical handling to GenerateHOTP: input is UTF-8 decoded, trimmed, uppercased, whitespace-stripped, then passed to OTPAuth.Secret.fromBase32(). If that throws, this OperationError surfaces. Empty input yields a fresh random secret and does not trigger the error.","triggerScenarios":"Calling GenerateTOTP.run() with input containing non-base32 characters (0, 1, 8, 9, or symbols beyond whitespace), or malformed padding.","commonSituations":"Pasting a secret copied with formatting artifacts, or supplying a hex/base64 secret directly.","solutions":["Ensure the input contains only A-Z and 2-7 (spaces are stripped).","Convert hex/base64 secrets to base32 before input.","Remove '=' padding and dashes."],"exampleFix":"// before\ninput = \"JBSWY3DPEHPK3PXP==\"; // padding may be rejected\n// after\ninput = \"JBSWY3DPEHPK3PXP\";","handlingStrategy":"validation","validationCode":"const BASE32 = /^[A-Z2-7]+$/;\nconst cleaned = secretStr.toUpperCase().replace(/\\s+/g, \"\");\nif (secretStr && !BASE32.test(cleaned)) {\n  // do not call the operation; surface a base32 error to the user\n}","typeGuard":"function isValidBase32Secret(s) {\n  if (!s) return true;\n  return /^[A-Z2-7]+$/.test(s.toUpperCase().replace(/\\s+/g, \"\"));\n}","tryCatchPattern":"try {\n  return OTPAuth.Secret.fromBase32(cleaned);\n} catch {\n  throw new OperationError(\"Invalid secret. Use base32 A-Z and 2-7.\");\n}","preventionTips":["Normalize to uppercase and strip whitespace before passing.","Validate against /^[A-Z2-7]+$/ before invoking.","Convert hex/base64 secrets to base32 first."],"tags":["otp","base32","totp","user-input","argument-validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}