/* validate.c --- Validate consistency of DIGEST-MD5 tokens.
 * Copyright (C) 2004-2026 Simon Josefsson
 *
 * This file is part of GNU SASL Library.
 *
 * GNU SASL Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * GNU SASL Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GNU SASL Library; if not, see
 * <https://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

/* Get prototypes. */
#include "validate.h"

/* Get strcmp, strlen. */
#include <string.h>

int
digest_md5_validate_challenge (digest_md5_challenge *c)
{
  /* This directive is required and MUST appear exactly once; if
     not present, or if multiple instances are present, the
     client should abort the authentication exchange. */
  if (!c->nonce)
    return -1;

  /* This directive must be present exactly once if "auth-conf" is
     offered in the "qop-options" directive */
  if (c->ciphers && !(c->qops & DIGEST_MD5_QOP_AUTH_CONF))
    return -1;
  if (!c->ciphers && (c->qops & DIGEST_MD5_QOP_AUTH_CONF))
    return -1;

  return 0;
}

int
digest_md5_validate_response (digest_md5_response *r)
{
  /* This directive is required and MUST be present exactly
     once; otherwise, authentication fails. */
  if (!r->username)
    return -1;

  /* This directive is required and MUST be present exactly
     once; otherwise, authentication fails. */
  if (!r->nonce)
    return -1;

  /* This directive is required and MUST be present exactly once;
     otherwise, authentication fails. */
  if (!r->cnonce)
    return -1;

  /* This directive is required and MUST be present exactly once;
     otherwise, or if the value is 0, authentication fails. */
  if (!r->nc)
    return -1;

  /* This directive is required and MUST be present exactly
     once; if multiple instances are present, the client MUST
     abort the authentication exchange. */
  if (!r->digesturi)
    return -1;

  /* This directive is required and MUST be present exactly
     once; otherwise, authentication fails. */
  if (!*r->response)
    return -1;

  if (strlen (r->response) != DIGEST_MD5_RESPONSE_LENGTH)
    return -1;

  /* This directive MUST appear exactly once if "auth-conf" is
     negotiated; if required and not present, authentication fails.
     If the client recognizes no cipher and the server only advertised
     "auth-conf" in the qop option, the client MUST abort the
     authentication exchange.  */
  if (r->qop == DIGEST_MD5_QOP_AUTH_CONF && !r->cipher)
    return -1;
  if (r->qop != DIGEST_MD5_QOP_AUTH_CONF && r->cipher)
    return -1;

  return 0;
}

int
digest_md5_validate_finish (digest_md5_finish *f)
{
  if (!*f->rspauth)
    return -1;

  /* A string of 32 hex digits */
  if (strlen (f->rspauth) != DIGEST_MD5_RESPONSE_LENGTH)
    return -1;

  return 0;
}

int
digest_md5_validate (digest_md5_challenge *c, digest_md5_response *r)
{
  if (!c->nonce || !r->nonce)
    return -1;

  if (strcmp (c->nonce, r->nonce) != 0)
    return -1;

  if (r->nc != 1)
    return -1;

  if (!c->utf8 && r->utf8)
    return -1;

  if (!((c->qops ? c->qops : DIGEST_MD5_QOP_AUTH) &
	(r->qop ? r->qop : DIGEST_MD5_QOP_AUTH)))
    return -1;

  if ((r->qop & DIGEST_MD5_QOP_AUTH_CONF) && !(c->ciphers & r->cipher))
    return -1;

  /* FIXME: Check more? */

  return 0;
}
