Branch: Tag:

2017-11-13

2017-11-13 16:05:44 by Stephen R. van den Berg <srb@cuci.nl>

Crypto.SCRAM: Made robust against rogue input and fix caching mechanism.

8:   //!   //! @[client_1] -> @[server_1] -> @[server_2] -> @[client_2] ->   //! @[server_3] -> @[client_3] + //! + //! @note + //! This implementation does not pretend to support the full protocol. + //! Most notably optional extension arguments are not supported (yet).      #pike __REAL_VERSION__   #pragma strict_types
20:   constant ClientKey = "Client Key";   constant ServerKey = "Server Key";    + private string(7bit) encode64(string(8bit) raw) { +  return MIME.encode_base64(raw, 1); + } +    //! Step 0 in the SCRAM handshake, prior to creating the object,   //! you need to have agreed with your peer on the hashfunction to be used.   //!
56:   //! @seealso   //! @[client_2]   string(7bit) client_1(void|string username) { -  cnonce = MIME.encode_base64(random_string(18)); +  cnonce = encode64(random_string(18));    return [string(7bit)](first = [string(8bit)]sprintf("n,,n=%s,r=%s",    username && username != "" ? Standards.IDNA.to_ascii(username, 1) : "",    cnonce));
68:   //! The received client-first request from the client.   //!   //! @returns - //! The username specified by the client. + //! The username specified by the client. Returns null + //! if the response could not be parsed.   //!   //! @seealso   //! @[server_2]   string server_1(Stdio.Buffer|string(8bit) line) {    constant format = "n,,n=%s,r=%s";    string username, r; -  +  catch {    first = line[3..];    [username, r] = stringp(line)    ? array_sscanf([string]line, format)    : [array(string)](line->sscanf(format));    cnonce = [string(8bit)]r; -  return Standards.IDNA.to_unicode(username); +  r = Standards.IDNA.to_unicode(username); +  }; +  return r;   }      //! Server-side step 2 in the SCRAM handshake.
99:   //! @[server_3]   string(7bit) server_2(string(8bit) salt, int iters) {    string response = sprintf("r=%s,s=%s,i=%d", -  cnonce += MIME.encode_base64(random_string(18)), -  MIME.encode_base64(salt), -  iters); +  cnonce += encode64(random_string(18)), encode64(salt), iters);    first += "," + response + ",";    return [string(7bit)]response;   }
116:   //!   //! @returns   //! The client-final response to send to the server. If the response is - //! null, the server messed up the handshake. + //! null, the server sent something unacceptable or unparseable.   //!   //! @seealso   //! @[client_3]
124:    constant format = "r=%s,s=%s,i=%d";    string r, salt;    int iters; -  [r, salt, iters] = stringp(line) +  if (!catch([r, salt, iters] = stringp(line)    ? array_sscanf([string]line, format) -  : [array(string)](line->sscanf(format)); -  if (iters > 0 && has_prefix(r, cnonce)) { +  : [array(string)](line->sscanf(format))) +  && iters > 0 +  && has_prefix(r, cnonce)) {    line = [string(8bit)]sprintf("c=biws,r=%s", r);    r = sprintf("%s,r=%s,s=%s,i=%d,%s", first[3..], r, salt, iters, line);    if (pass != "")    pass = Standards.IDNA.to_ascii(pass);    salt = MIME.decode_base64(salt); -  if (!(first = .SCRAM_get_salted_password(H, pass, salt, iters))) { +  cnonce = sprintf("%s,%s,%d", pass, salt, iters); +  if (!(first = .SCRAM_get_salted_password(H, cnonce))) {    first = [string(8bit)]H->pbkdf2(pass, salt, iters, H->digest_size()); -  .SCRAM_set_salted_password(first, H, pass, salt, iters); +  .SCRAM_set_salted_password(first, H, cnonce);    }    Crypto.MAC.State hmacfirst = HMAC(first);    first = 0; // Free memory    salt = hmacfirst([string(8bit)]ClientKey);    salt = sprintf("%s,p=%s", line, -  MIME.encode_base64(salt +  encode64(salt    ^ HMAC(H->hash([string(8bit)]salt))([string(8bit)]r)));    cnonce = HMAC(hmacfirst([string(8bit)]ServerKey))([string(8bit)]r);    } else
160:   //!   //! @returns   //! The server-final response to send to the client. If the response - //! is null, the client did not supply the correct credentials. + //! is null, the client did not supply the correct credentials or + //! the response was unparseable.   string(7bit) server_3(Stdio.Buffer|string(8bit) line,    string(8bit) salted_password) {    constant format = "c=biws,r=%s,p=%s";    string r, p, response; -  [r, p] = stringp(line) +  if (!catch([r, p] = stringp(line)    ? array_sscanf([string]line, format) -  : [array(string)](line->sscanf(format)); -  if (r == cnonce) { +  : [array(string)](line->sscanf(format))) +  && r == cnonce) {    first += sprintf("c=biws,r=%s", r);    Crypto.MAC.State hmacfirst = HMAC(salted_password);    r = hmacfirst([string(8bit)]ClientKey);    if (MIME.decode_base64(p)    == [string(8bit)](r ^ HMAC(H->hash([string(8bit)]r))(first))) -  response = sprintf("v=%s", MIME.encode_base64(HMAC( +  response = sprintf("v=%s", encode64(HMAC(    hmacfirst([string(8bit)]ServerKey))(first)));    }    return response;
193:   int(0..1) client_3(Stdio.Buffer|string(8bit) line) {    constant format = "v=%s";    string(8bit) v; -  [v] = stringp(line) -  ? array_sscanf(line, format) : line->sscanf(format); -  return MIME.decode_base64(v) == cnonce; +  return !catch([v] = stringp(line) +  ? array_sscanf(line, format) +  : line->sscanf(format)) +  && MIME.decode_base64(v) == cnonce;   }