authenticate method

bool authenticate(
  1. SipMethod method,
  2. Challenge challenge, [
  3. dynamic ruri,
  4. String? cnonce,
  5. String? body,
])

Performs Digest authentication given a SIP request and the challenge received in a response to that request. Returns true if auth was successfully generated, false otherwise.

Implementation

bool authenticate(SipMethod method, Challenge challenge,
      [dynamic ruri, String? cnonce, String? body]) {
    _algorithm = challenge.algorithm;
    _realm = challenge.realm;
    _nonce = challenge.nonce;
    _opaque = challenge.opaque;
    _stale = challenge.stale;

    if (_algorithm != null) {
      if (_algorithm != 'MD5') {
        logger.error(
            'authenticate() | challenge with Digest algorithm different than "MD5", authentication aborted');

        return false;
      }
    } else {
      _algorithm = 'MD5';
    }

    if (_nonce == null) {
      logger.error(
          'authenticate() | challenge without Digest nonce, authentication aborted');

      return false;
    }

    if (_realm == null) {
      logger.error(
          'authenticate() | challenge without Digest realm, authentication aborted');

      return false;
    }

    // If no plain SIP password is provided.
    if (_credentials.password == null) {
      // If ha1 is not provided we cannot authenticate.
      if (_credentials.ha1 == null) {
        logger.error(
            'authenticate() | no plain SIP password nor ha1 provided, authentication aborted');

        return false;
      }

      // If the realm does not match the stored realm we cannot authenticate.
      if (_credentials.realm != _realm) {
        logger.error(
            'authenticate() | no plain SIP password, and stored "realm" does not match the given "realm", cannot authenticate [stored:"${_credentials.realm}", given:"$_realm"]');

        return false;
      }
    }

    // 'qop' can contain a list of values (Array). Let's choose just one.
    if (challenge.qop != null && challenge.qop.isNotEmpty) {
      if (challenge.qop.indexOf('auth-int') > -1) {
        _qop = 'auth-int';
      } else if (challenge.qop.indexOf('auth') > -1) {
        _qop = 'auth';
      } else {
        // Otherwise 'qop' is present but does not contain 'auth' or 'auth-int', so abort here.
        logger.error(
            'authenticate() | challenge without Digest qop different than "auth" or "auth-int", authentication aborted');

        return false;
      }
    } else {
      _qop = null;
    }

    // Fill other attributes.

    _method = method;
    _uri = ruri ?? '';
    _cnonce = cnonce ?? utils.createRandomToken(12);
    _nc += 1;
    String hex = _nc.toRadixString(16);

    _ncHex = '00000000'.substring(0, 8 - hex.length) + hex;

    // Nc-value = 8LHEX. Max value = 'FFFFFFFF'.
    if (_nc == 4294967296) {
      _nc = 1;
      _ncHex = '00000001';
    }

    // Calculate the Digest "response" value.

    // If we have plain SIP password then regenerate ha1.
    if (_credentials.password != null) {
      // HA1 = MD5(A1) = MD5(username:realm:password).
      _ha1 = utils.calculateMD5(
          '${_credentials.username}:$_realm:${_credentials.password}');
    }
    // Otherwise reuse the stored ha1.
    else {
      _ha1 = _credentials.ha1;
    }

    String a2;
    String ha2;

    if (_qop == 'auth') {
      // HA2 = MD5(A2) = MD5(method:digestURI).
      a2 = '${SipMethodHelper.getName(_method)}:$_uri';
      ha2 = utils.calculateMD5(a2);

      logger.debug('authenticate() | using qop=auth [a2:$a2]');

      // Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).
      _response =
          utils.calculateMD5('$_ha1:$_nonce:$_ncHex:$_cnonce:auth:$ha2');
    } else if (_qop == 'auth-int') {
      // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody)).
      a2 =
          '${SipMethodHelper.getName(_method)}:$_uri:${utils.calculateMD5(body ?? '')}';
      ha2 = utils.calculateMD5(a2);

      logger.debug('authenticate() | using qop=auth-int [a2:$a2]');

      // Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).
      _response =
          utils.calculateMD5('$_ha1:$_nonce:$_ncHex:$_cnonce:auth-int:$ha2');
    } else if (_qop == null) {
      // HA2 = MD5(A2) = MD5(method:digestURI).
      a2 = '${SipMethodHelper.getName(_method)}:$_uri';
      ha2 = utils.calculateMD5(a2);

      logger.debug('authenticate() | using qop=null [a2:$a2]');

      // Response = MD5(HA1:nonce:HA2).
      _response = utils.calculateMD5('$_ha1:$_nonce:$ha2');
    }

    logger.debug('authenticate() | response generated');

    return true;
  }