|
|
|
@ -26,7 +26,7 @@ with Pontarius XMPP. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
-- TODO: Host is assumed to be ISO 8859-1; make list of assumptions. |
|
|
|
-- TODO: Host is assumed to be ISO 8859-1; make list of assumptions. |
|
|
|
-- TODO: Can it contain newline characters? |
|
|
|
-- TODO: Can it contain newline characters? |
|
|
|
|
|
|
|
|
|
|
|
module Network.XMPP.SASL (replyToChallenge) where |
|
|
|
module Network.XMPP.SASL (replyToChallenge, saltedPassword, clientKey, storedKey, authMessage, clientSignature, clientProof) where |
|
|
|
|
|
|
|
|
|
|
|
import Prelude hiding (concat, zipWith) |
|
|
|
import Prelude hiding (concat, zipWith) |
|
|
|
import Data.ByteString.Internal (c2w) |
|
|
|
import Data.ByteString.Internal (c2w) |
|
|
|
@ -41,13 +41,19 @@ import Text.StringPrep (StringPrepProfile (..), a1, b1, c12, c21, c22, c3, c4, c |
|
|
|
import Data.Ranges (inRanges, ranges) |
|
|
|
import Data.Ranges (inRanges, ranges) |
|
|
|
|
|
|
|
|
|
|
|
import Crypto.HMAC (MacKey (MacKey), hmac) |
|
|
|
import Crypto.HMAC (MacKey (MacKey), hmac) |
|
|
|
import Crypto.Hash.SHA1 (SHA1) |
|
|
|
import Crypto.Hash.SHA1 (SHA1, hash) |
|
|
|
import Data.Bits (xor) |
|
|
|
import Data.Bits (xor) |
|
|
|
import Data.ByteString () |
|
|
|
import Data.ByteString () |
|
|
|
import Data.ByteString.Lazy (ByteString, concat, pack, toChunks, zipWith) |
|
|
|
import Data.ByteString.Lazy (ByteString, concat, fromChunks, pack, toChunks, zipWith) |
|
|
|
import Data.Serialize (Serialize, encodeLazy) |
|
|
|
import Data.Serialize (Serialize, encodeLazy) |
|
|
|
import Data.Serialize.Put (putWord32be, runPutLazy) |
|
|
|
import Data.Serialize.Put (putWord32be, runPutLazy) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Data.Maybe (fromJust, isJust) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import qualified Data.Text as DT |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Text.StringPrep (runStringPrep) |
|
|
|
|
|
|
|
|
|
|
|
data Challenge1Error = C1MultipleCriticalAttributes | |
|
|
|
data Challenge1Error = C1MultipleCriticalAttributes | |
|
|
|
C1NotAllParametersPresent | |
|
|
|
C1NotAllParametersPresent | |
|
|
|
C1SomeParamtersPresentMoreThanOnce | |
|
|
|
C1SomeParamtersPresentMoreThanOnce | |
|
|
|
@ -115,14 +121,45 @@ hi str salt i | i > 0 = xorUs $ us (concat [salt, runPutLazy $ putWord32be 1]) i |
|
|
|
|
|
|
|
|
|
|
|
-- Calculates the U's (U1 ... Ui) using the HMAC algorithm |
|
|
|
-- Calculates the U's (U1 ... Ui) using the HMAC algorithm |
|
|
|
us :: ByteString -> Integer -> [ByteString] |
|
|
|
us :: ByteString -> Integer -> [ByteString] |
|
|
|
us a 1 = [encodeLazy $ (hmac (MacKey (head $ toChunks str)) a :: SHA1)] |
|
|
|
us a 1 = [encodeLazy (hmac (MacKey (head $ toChunks str)) a :: SHA1)] |
|
|
|
us a x = [encodeLazy $ (hmac (MacKey (head $ toChunks str)) a :: SHA1)] ++ (us (encodeLazy $ (hmac (MacKey (head $ toChunks str)) a :: SHA1)) (x - 1)) |
|
|
|
us a x = [encodeLazy (hmac (MacKey (head $ toChunks str)) a :: SHA1)] ++ (us (encodeLazy (hmac (MacKey (head $ toChunks str)) a :: SHA1)) (x - 1)) |
|
|
|
|
|
|
|
|
|
|
|
-- XORs the ByteStrings: U1 XOR U2 XOR ... XOR Ui |
|
|
|
-- XORs the ByteStrings: U1 XOR U2 XOR ... XOR Ui |
|
|
|
xorUs :: [ByteString] -> ByteString |
|
|
|
xorUs :: [ByteString] -> ByteString |
|
|
|
xorUs (b:bs) = foldl (\ x y -> pack $ zipWith xor x y) b bs |
|
|
|
xorUs (b:bs) = foldl (\ x y -> pack $ zipWith xor x y) b bs |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saltedPassword :: String -> ByteString -> Integer -> Maybe ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saltedPassword password salt i = if isJust password' then Just $ hi (DBLC.pack $ DT.unpack $ fromJust password') salt i else Nothing |
|
|
|
|
|
|
|
where |
|
|
|
|
|
|
|
password' = runStringPrep saslprepProfile (DT.pack password) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientKey :: ByteString -> ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientKey sp = encodeLazy (hmac (MacKey (head $ toChunks sp)) (DBLC.pack "Client Key") :: SHA1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
storedKey :: ByteString -> ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
storedKey ck = fromChunks [hash $ head $ toChunks ck] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
authMessage :: String -> String -> String -> ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
authMessage cfmb sfm cfmwp = DBLC.pack $ cfmb ++ "," ++ sfm ++ "," ++ cfmwp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientSignature :: ByteString -> ByteString -> ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientSignature sk am = encodeLazy (hmac (MacKey (head $ toChunks sk)) am :: SHA1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientProof :: ByteString -> ByteString -> ByteString |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientProof ck cs = pack $ zipWith xor ck cs |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- TODO: Implement SCRAM. |
|
|
|
-- TODO: Implement SCRAM. |
|
|
|
|
|
|
|
|
|
|
|
replyToChallenge = replyToChallenge |
|
|
|
replyToChallenge = replyToChallenge |
|
|
|
@ -138,7 +175,7 @@ stripQuotations s | (head s == '"') && (last s == '"') = tail $ init s |
|
|
|
|
|
|
|
|
|
|
|
saslprepProfile :: StringPrepProfile |
|
|
|
saslprepProfile :: StringPrepProfile |
|
|
|
|
|
|
|
|
|
|
|
saslprepProfile = Profile { maps = [\ char -> if char `inRanges` (ranges c12) then singleton '\x0020' else empty, b1] |
|
|
|
saslprepProfile = Profile { maps = [\ char -> if char `inRanges` (ranges c12) then singleton '\x0020' else singleton char, b1] |
|
|
|
, shouldNormalize = True |
|
|
|
, shouldNormalize = True |
|
|
|
, prohibited = [a1] ++ [c12, c21, c22, c3, c4, c5, c6, c7, c8, c9] |
|
|
|
, prohibited = [a1] ++ [c12, c21, c22, c3, c4, c5, c6, c7, c8, c9] |
|
|
|
, shouldCheckBidi = True } |
|
|
|
, shouldCheckBidi = True } |
|
|
|
|