protected function verifyHmac($str, $method, $password, $throw = false) {
        // Grab the signature from the string.
        $signature = $this->popString($str);
        if (!$signature) {
            return $this->exception($throw, "The signature is missing.", 403);
        }
        $signature = static::base64urlDecode($signature);

        // Recalculate the signature to compare.
        $calcSignature = hash_hmac($method, $str, $password, true);

        if (strlen($signature) !== strlen($calcSignature)) {
            return $this->exception($throw, "The signature is invalid.", 403);
        }

        // Do a double hmac comparison to prevent timing attacks.
        // https://www.isecpartners.com/blog/2011/february/double-hmac-verification.aspx
        $dblSignature = hash_hmac($method, $signature, $password, true);
        $dblCalcSignature = hash_hmac($method, $calcSignature, $password, true);

        if ($dblSignature !== $dblCalcSignature) {
            return $this->exception($throw, "The signature is invalid.", 403);
        }

        // Grab the timestamp and verify it.
        $timestamp = $this->popString($str);
        if (!$this->verifyTimestamp($timestamp, $throw)) {
            return null;
        }

        return $str;
    }