|
|
|
@ -27,6 +27,7 @@ import qualified Data.Text.Encoding as Text |
|
|
|
import Data.Word(Word8) |
|
|
|
import Data.Word(Word8) |
|
|
|
|
|
|
|
|
|
|
|
import Network.Xmpp.Sasl.Common |
|
|
|
import Network.Xmpp.Sasl.Common |
|
|
|
|
|
|
|
import Network.Xmpp.Sasl.StringPrep |
|
|
|
import Network.Xmpp.Sasl.Types |
|
|
|
import Network.Xmpp.Sasl.Types |
|
|
|
|
|
|
|
|
|
|
|
-- | Bit-wise xor of byte strings |
|
|
|
-- | Bit-wise xor of byte strings |
|
|
|
@ -57,12 +58,21 @@ scram :: (Crypto.Hash ctx hash) |
|
|
|
-> Maybe Text.Text -- ^ authorization ID |
|
|
|
-> Maybe Text.Text -- ^ authorization ID |
|
|
|
-> Text.Text -- ^ password |
|
|
|
-> Text.Text -- ^ password |
|
|
|
-> SaslM () |
|
|
|
-> SaslM () |
|
|
|
scram hashToken authcid authzid' password = do |
|
|
|
scram hashToken authcid authzid password = case credentials of |
|
|
|
|
|
|
|
Nothing -> throwError $ AuthStringPrepError |
|
|
|
|
|
|
|
Just (ac, az, pw) -> scramhelper hashToken ac az pw |
|
|
|
|
|
|
|
where |
|
|
|
|
|
|
|
credentials = do |
|
|
|
|
|
|
|
ac <- normalizeUsername authcid |
|
|
|
|
|
|
|
az <- case authzid of |
|
|
|
|
|
|
|
Nothing -> Just Nothing |
|
|
|
|
|
|
|
Just az' -> Just <$> normalizeUsername az' |
|
|
|
|
|
|
|
pw <- normalizePassword password |
|
|
|
|
|
|
|
return (ac, az, pw) |
|
|
|
|
|
|
|
scramhelper hashToken authcid authzid' password = do |
|
|
|
cnonce <- liftIO $ makeNonce |
|
|
|
cnonce <- liftIO $ makeNonce |
|
|
|
saslInit "SCRAM-SHA-1" (Just $ cFirstMessage cnonce) |
|
|
|
saslInit "SCRAM-SHA-1" (Just $ cFirstMessage cnonce) |
|
|
|
liftIO $ putStrLn "pulling challenge" |
|
|
|
|
|
|
|
sFirstMessage <- saslFromJust =<< pullChallenge |
|
|
|
sFirstMessage <- saslFromJust =<< pullChallenge |
|
|
|
liftIO $ putStrLn "pulled challenge" |
|
|
|
|
|
|
|
pairs <- toPairs sFirstMessage |
|
|
|
pairs <- toPairs sFirstMessage |
|
|
|
(nonce, salt, ic) <- fromPairs pairs cnonce |
|
|
|
(nonce, salt, ic) <- fromPairs pairs cnonce |
|
|
|
let (cfm, v) = cFinalMessageAndVerifier nonce salt ic sFirstMessage cnonce |
|
|
|
let (cfm, v) = cFinalMessageAndVerifier nonce salt ic sFirstMessage cnonce |
|
|
|
@ -77,14 +87,14 @@ scram hashToken authcid authzid' password = do |
|
|
|
hash str = encode hashToken $ Crypto.hash' str |
|
|
|
hash str = encode hashToken $ Crypto.hash' str |
|
|
|
hmac key str = encode hashToken $ Crypto.hmac' (Crypto.MacKey key) str |
|
|
|
hmac key str = encode hashToken $ Crypto.hmac' (Crypto.MacKey key) str |
|
|
|
|
|
|
|
|
|
|
|
authzid = (\z -> "a=" +++ normalize z) <$> authzid' |
|
|
|
authzid = (\z -> "a=" +++ Text.encodeUtf8 z) <$> authzid' |
|
|
|
gs2CbindFlag = "n" -- we don't support channel binding yet |
|
|
|
gs2CbindFlag = "n" -- we don't support channel binding yet |
|
|
|
gs2Header = merge $ [ gs2CbindFlag |
|
|
|
gs2Header = merge $ [ gs2CbindFlag |
|
|
|
, maybe "" id authzid |
|
|
|
, maybe "" id authzid |
|
|
|
, "" |
|
|
|
, "" |
|
|
|
] |
|
|
|
] |
|
|
|
cbindData = "" -- we don't support channel binding yet |
|
|
|
cbindData = "" -- we don't support channel binding yet |
|
|
|
cFirstMessageBare cnonce = merge [ "n=" +++ normalize authcid |
|
|
|
cFirstMessageBare cnonce = merge [ "n=" +++ Text.encodeUtf8 authcid |
|
|
|
, "r=" +++ cnonce] |
|
|
|
, "r=" +++ cnonce] |
|
|
|
cFirstMessage cnonce = gs2Header +++ cFirstMessageBare cnonce |
|
|
|
cFirstMessage cnonce = gs2Header +++ cFirstMessageBare cnonce |
|
|
|
|
|
|
|
|
|
|
|
@ -109,7 +119,7 @@ scram hashToken authcid authzid' password = do |
|
|
|
where |
|
|
|
where |
|
|
|
cFinalMessageWOProof = merge [ "c=" +++ B64.encode gs2Header |
|
|
|
cFinalMessageWOProof = merge [ "c=" +++ B64.encode gs2Header |
|
|
|
, "r=" +++ nonce] |
|
|
|
, "r=" +++ nonce] |
|
|
|
saltedPassword = hi (normalize password) salt ic |
|
|
|
saltedPassword = hi (Text.encodeUtf8 password) salt ic |
|
|
|
clientKey = hmac saltedPassword "Client Key" |
|
|
|
clientKey = hmac saltedPassword "Client Key" |
|
|
|
storedKey = hash clientKey |
|
|
|
storedKey = hash clientKey |
|
|
|
authMessage = merge [ cFirstMessageBare cnonce |
|
|
|
authMessage = merge [ cFirstMessageBare cnonce |
|
|
|
@ -127,13 +137,12 @@ scram hashToken authcid authzid' password = do |
|
|
|
u1 = hmac str (salt +++ (BS.pack [0,0,0,1])) |
|
|
|
u1 = hmac str (salt +++ (BS.pack [0,0,0,1])) |
|
|
|
us = iterate (hmac str) u1 |
|
|
|
us = iterate (hmac str) u1 |
|
|
|
|
|
|
|
|
|
|
|
normalize = Text.encodeUtf8 . id -- TODO: stringprep |
|
|
|
|
|
|
|
base64 = B64.encode |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- | 'scram' spezialised to the SHA-1 hash function, packaged as a SaslHandler |
|
|
|
-- | 'scram' spezialised to the SHA-1 hash function, packaged as a SaslHandler |
|
|
|
scramSha1 :: SaslM Text.Text -> SaslHandler |
|
|
|
scramSha1 :: Text.Text -- ^ username |
|
|
|
scramSha1 passwd = ("SCRAM-SHA-1" |
|
|
|
-> Maybe Text.Text -- ^ authorization ID |
|
|
|
, \_hostname authcid authzid -> do |
|
|
|
-> Text.Text -- ^ password |
|
|
|
pw <- passwd |
|
|
|
-> SaslHandler |
|
|
|
scram (hashToken :: Crypto.SHA1) authcid authzid pw |
|
|
|
scramSha1 authcid authzid passwd = |
|
|
|
|
|
|
|
("SCRAM-SHA-1" |
|
|
|
|
|
|
|
, scram (hashToken :: Crypto.SHA1) authcid authzid passwd |
|
|
|
) |
|
|
|
) |
|
|
|
|