{"record":{"id":"b58d3c607faf0413","repo":"jstedfast/MailKit","slug":"invalidchallenge-challenge-contained-an-invalid-nonce","errorCode":"InvalidChallenge","errorMessage":"Challenge contained an invalid nonce.","messagePattern":"Challenge contained an invalid nonce\\.","errorType":"error_code","errorClass":"SaslException","httpStatus":null,"severity":"error","filePath":"MailKit/Security/SaslMechanismScramBase.cs","lineNumber":354,"sourceCode":"\t\t\t\tif (token == null)\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.MissingChallenge, \"Server response did not contain any authentication data.\");\n\n\t\t\t\tserver = Encoding.UTF8.GetString (token, startIndex, length);\n\t\t\t\tvar tokens = ParseServerChallenge (server);\n\t\t\t\tstring? salt, nonce, iterations;\n\t\t\t\tint count;\n\n\t\t\t\tif (!tokens.TryGetValue ('s', out salt))\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, \"Challenge did not contain a salt.\");\n\n\t\t\t\tif (!tokens.TryGetValue ('r', out nonce))\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, \"Challenge did not contain a nonce.\");\n\n\t\t\t\tif (!tokens.TryGetValue ('i', out iterations))\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, \"Challenge did not contain an iteration count.\");\n\n\t\t\t\tif (!nonce.StartsWith (cnonce!, StringComparison.Ordinal))\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, \"Challenge contained an invalid nonce.\");\n\n\t\t\t\tif (!int.TryParse (iterations, NumberStyles.None, CultureInfo.InvariantCulture, out count) || count < 1)\n\t\t\t\t\tthrow new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, \"Challenge contained an invalid iteration count.\");\n\n\t\t\t\tvar password = Encoding.UTF8.GetBytes (SaslPrep (Credentials.Password));\n\t\t\t\tsalted = Hi (password, Convert.FromBase64String (salt), count);\n\t\t\t\tArray.Clear (password, 0, password.Length);\n\n\t\t\t\tinput = GetChannelBindingInput (channelBindingKind, AuthorizationId);\n\t\t\t\tvar inputBuffer = Encoding.ASCII.GetBytes (input);\n\t\t\t\tstring base64;\n\n\t\t\t\tif (SupportsChannelBinding && channelBindingToken != null) {\n\t\t\t\t\tvar binding = new byte[inputBuffer.Length + channelBindingToken.Length];\n\n\t\t\t\t\tBuffer.BlockCopy (inputBuffer, 0, binding, 0, inputBuffer.Length);\n\t\t\t\t\tBuffer.BlockCopy (channelBindingToken, 0, binding, inputBuffer.Length, channelBindingToken.Length);\n","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/jstedfast/MailKit/blob/9d3859a7855e3e17582c07fd01972b8e262bf176/MailKit/Security/SaslMechanismScramBase.cs#L336-L372","documentation":"After extracting the SCRAM server nonce ('r'), MailKit verifies that it starts with the client nonce sent in the client-first message. If not, SaslException with SaslErrorCode.InvalidChallenge and \"Challenge contained an invalid nonce\" is thrown (SaslMechanismScramBase.cs:354). RFC 5802 requires server_nonce = client_nonce + server_nonce, so a mismatch indicates a broken or malicious server.","triggerScenarios":"Calling Challenge() on a SCRAM mechanism where the server's r=... value does not begin with the exact cnonce the client generated (e.g. server returns a wholly different nonce, or a replayed stale challenge).","commonSituations":"Custom/buggy SCRAM server implementations that ignore the client nonce, challenge replay by a man-in-the-middle, or caching proxies serving stale challenges.","solutions":["Capture the client-first and server-first messages (enable protocol logging, e.g. IProtocolLogger) and compare nonce prefixes","Confirm the server implements RFC 5802 nonce concatenation; upgrade or patch the server","Rule out MITM/replay by using TLS for the connection","Fall back to another mechanism if the server's SCRAM nonce logic is broken"],"exampleFix":"// before\nclient.Authenticate (new SaslMechanismScramSha256 (\"user\", \"pass\")); // non-conformant server nonce\n// after\ntry {\n    client.Authenticate (new SaslMechanismScramSha256 (\"user\", \"pass\"));\n} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.InvalidChallenge && ex.Message.Contains (\"nonce\")) {\n    client.Authenticate (new SaslMechanismPlain (\"user\", \"pass\")); // over TLS\n}","handlingStrategy":"try-catch","validationCode":"// Enforce TLS so nonce mismatches cannot come from tampering:\nif (!client.IsSecure)\n    await client.ConnectAsync (host, port, SecureSocketOptions.SslOnConnect);","typeGuard":null,"tryCatchPattern":"try {\n    client.Authenticate (scramMechanism);\n} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.InvalidChallenge &&\n                                 ex.Message.Contains (\"invalid nonce\")) {\n    // server nonce does not extend the client nonce\n}","preventionTips":["Always use SCRAM over TLS (protects the nonce exchange)","Verify server RFC 5802 compliance during integration testing","Use protocol logging to compare client vs server nonces","Treat repeated nonce failures as a possible MITM indicator"],"tags":["sasl","scram","authentication","nonce","security","mailkit"],"backgroundTag":"unexpected-response-shape","analyzedSha":"9d3859a7855e3e17582c07fd01972b8e262bf176","analyzedAt":"2026-09-15T15:46:11.592Z","contentChangedAt":"2026-09-15T15:46:11.592Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}