Аутентификация XMPP SASL SCRAM-SHA1

недавно я смог получить аутентификацию MD5, работающую для потоков XMPP в Swift IOS, следуя инструкциям на следующих двух веб-сайтах (я использовал функцию CC-MD5 библиотеки CommonCrypto C Apple для фактического хэширования):

http://wiki.xmpp.org/web/SASLandDIGEST-MD5

http://www.deusty.com/2007/09/example-please.html

Я ищу аналогичное объяснение того, как получить другой хэшированный SASL схемы аутентификации работают, особенно SCRAM-SHA1. Я нашел чиновника RFC5802 документ, но у меня много проблем с его пониманием (это не относится и к XMPP). Я был бы признателен за более простое объяснение или простой читаемый код (C, PHP, C++, Javascript, Java), специфичный для аутентификации XMPP, который не использует библиотеки ни для чего, кроме фактического хэширования.

Я заинтересован в понимании процесса и не собираюсь использовать ios XMPP-Framework. Любая помощь будет оценена.

1 ответов


ПРОВАЛИВАЙ-ША-1

основной обзор того, как работает этот механизм:

  • клиент отправляет имя пользователя, которое он хочет аутентифицировать как.
  • сервер отправляет обратно соль для этого пользователя и количество итераций (либо путем их генерации, либо путем поиска их в своей базе данных для данного имени пользователя).
  • клиент хэширует пароль с солью для заданного числа итераций.
  • клиент отправляет результат обратно.
  • сервер выполняет изменение хэширования и отправляет его результат обратно клиенту, поэтому клиент также может проверить, что у сервера был пароль/хэш пароля.

криптографические алгоритмы, которые вам понадобятся, - это SHA-1, HMAC с SHA-1 и PBKDF2 с с SHA-1. Вы должны посмотреть, как использовать их в своем языке/фреймворке, поскольку я не рекомендую реализовывать их с нуля.

In деталь

  1. сначала нормализуйте пароль (используя SASLprep), это будет normalizedPassword. Это необходимо для того, чтобы кодировка UTF8 не могла содержать варианты одного и того же пароля.
  2. выбрать случайную строку (например, 32 байта в шестнадцатиричной кодировке). Это будет clientNonce.
  3. на initialMessage is "n=" .. username .. ",r=" .. clientNonce (я использую .. для конкатенации строк).
  4. клиент добавляет заголовок GS2 ("n,,") к initialMessage и из base64 кодирует результат. Он отправляет это как свое первое сообщение:

    <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1">
        biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl
    </auth>
    
  5. сервер отвечает вызовом. Данные вызова закодированы в base64:

    <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY=
    </challenge>
    
  6. клиент base64 расшифровывает его:

    r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
    
  7. клиент анализирует это:

    • r= это serverNonce. Клиент должен убедиться, что он начинается с clientNonce он отправил свой инициал сообщение.
    • s= это salt, base64 кодируется (да, это base64-кодируется дважды!)
    • i= это число итераций,i.
  8. клиент вычисляет:

    clientFinalMessageBare = "c=biws,r=" .. serverNonce
    saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i)
    clientKey = HMAC-SHA-1(saltedPassword, "Client Key")
    storedKey = SHA-1(clientKey)
    authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare
    clientSignature = HMAC-SHA-1(storedKey, authMessage)
    clientProof = clientKey XOR clientSignature
    serverKey = HMAC-SHA-1(saltedPassword, "Server Key")
    serverSignature = HMAC-SHA-1(serverKey, authMessage)
    clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
    
  9. клиент base64 кодирует clientFinalMessage и отправляет его в качестве ответа:

    <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0=
    </response>
    
  10. если все прошло хорошо, вы получите <success> ответ от сервер:

     <success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
         dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289
     </success>
    
  11. Base64 декодировано это содержит:

     v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
    
  12. клиент должен убедиться, что значение v является кодировкой base64 serverSignature.

дополнительно

это базовая версия алгоритма. Вы можете расширить его, чтобы сделать:

  • привязка канала. Это смешивает некоторые сведения из подключения TLS к процедуре для предотвращения MitM атаки.
  • хэшированных хранения. Если сервер всегда отправляет одно и то же salt и i значения, тогда клиент может хранить только saltedPassword вместо пароля пользователя. Это более безопасно (так как клиенту не нужно хранить пароль, просто трудно отменить соленый хэш) и быстрее, так как клиенту не нужно каждый раз выполнять растяжку ключей.

    сервер также может использовать хэшированное хранилище: сервер может хранить только salt, i, storedKey и serverKey. Более подробную информацию о том, что здесь.

  • возможно, также добавляя SCRAM-SHA-256 (хотя поддержка сервера кажется несуществующей).

подводные камни

некоторые "подводные камни":

  • не предполагайте ничего о длине nonces или salt (хотя если вы их генерируете, убедитесь, что они достаточно длинные и криптографически случайные).
  • на salt кодируется base64 и может содержать каких-либо данных (встроенный NULs).
  • не использовать SASLprep может отлично работать для людей, использующих пароли ASCII, но он может полностью сломать вход для людей, использующих другие скрипты.
  • на initialMessage часть authMessage не включает заголовок GS2 (в большинстве случаев это "n,,").

тест векторы

если вы хотите проверить свою реализацию, вот все промежуточные результаты для примера из RFC:

  • имя пользователя: user

  • пароль: pencil

  • клиент генерирует случайный код fyko+d2lbbFgONRv9qkxdawL

  • исходное сообщение: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL

  • сервер генерирует случайное nonce 3rfcNHYJY1ZVvWVs7j

  • сервер отвечает: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096

  • соль (hex): 4125c247e43ab1e93c6dff76

  • последнее сообщение клиента голое:c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j

  • соленый пароль (hex):1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d

  • ключ клиента (hex):e234c47bf6c36696dd6d852b99aaa2ba26555728

  • сохраненный ключ (hex):e9d94660c39d65c38fbad91c358f14da0eef2bd6

  • Auth сообщение:n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j

  • подпись клиента (hex):5d7138c486b0bfabdf49e3e2da8bd6e5c79db613

  • доказательство клиента (hex): bf45fcbf7073d93d022466c94321745fe1c8e13b

  • ключ сервера (hex):0fe09258b3ac852ba502cc62ba903eaacdbf7d31

  • подпись сервера (hex):ae617da6a57c4bbb2e0286568dae1d251905b0a4

  • клиент последнее сообщение: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=

  • сервер-последнее сообщение: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=

  • подпись сервера сервера (hex):ae617da6a57c4bbb2e0286568dae1d251905b0a4