packages feed

one-time-password-3.0.0.0: test/Test/Comparison.hs

{-# LANGUAGE OverloadedRecordDot #-}

module Test.Comparison where

import Chronos qualified
import Data.Base16.Types qualified as Base
import Data.ByteString.Base16 qualified as Base
import Sel.HMAC.SHA256 qualified as SHA256
import Test.Tasty
import Test.Tasty.HUnit

import Chronos (Time (..))
import Crypto.Hash.Algorithms qualified as Crypton
import Crypto.OTP qualified as Crypton
import Data.ByteString (StrictByteString)
import Data.Int (Int64)
import Data.Text.Display
import Data.Word (Word64)
import OTP.Commons
import OTP.TOTP qualified as TOTP
import Sel.HMAC.SHA512 qualified as SHA512
import Test.Utils
import Torsor

spec :: TestTree
spec =
  testGroup
    "Comparing outputs of other implementations"
    [ testGroup
        "Crypton"
        [ testCase "HMAC-SHA1 TOTP" testCryptonSHA1TOTP
        , testCase "HMAC-SH256 TOTP" testCryptonSHA256TOTP
        , testCase "HMAC-SH512 TOTP" testCryptonSHA512TOTP
        ]
    ]

testCryptonSHA1TOTP :: Assertion
testCryptonSHA1TOTP = do
  let hexKey = "e90cbae2d7d187f614806347cfd75002bd0db847451109599da507e8da88bf43"
  key <- assertRight $ SHA256.authenticationKeyFromHexByteString hexKey
  let cryptoniteKey = Base.decodeBase16 $ Base.assertBase16 @StrictByteString hexKey
  timestamp@(Time ns) <- Chronos.now
  let seconds = ns `quot` (10 ^ (9 :: Int64))
  let timeStep = scale 30 Chronos.second
  digits <- assertJust $ mkDigits 6

  let cryptoniteTOTP =
        Crypton.totp
          Crypton.defaultTOTPParams
          cryptoniteKey
          (fromIntegral @Int64 @Word64 seconds)

  let ownTotp = TOTP.totpSHA1 key timestamp timeStep digits

  assertEqual
    "OTPs are the same"
    (display ownTotp.code)
    (display cryptoniteTOTP)

testCryptonSHA256TOTP :: Assertion
testCryptonSHA256TOTP = do
  let hexKey = "e90cbae2d7d187f614806347cfd75002bd0db847451109599da507e8da88bf43"
  key <- assertRight $ SHA256.authenticationKeyFromHexByteString hexKey
  let cryptonKey = Base.decodeBase16 $ Base.assertBase16 @StrictByteString hexKey
  timestamp@(Time ns) <- Chronos.now
  let seconds = ns `quot` (10 ^ (9 :: Int64))
  let timeStep = scale 30 Chronos.second
  digits <- assertJust $ mkDigits 6
  totpParams <-
    assertRight $
      Crypton.mkTOTPParams
        Crypton.SHA256
        0
        30
        Crypton.OTP6
        Crypton.TwoSteps

  let cryptoniteTOTP =
        Crypton.totp
          totpParams
          cryptonKey
          (fromIntegral @Int64 @Word64 seconds)

  let ownTotp = TOTP.totpSHA256 key timestamp timeStep digits

  assertEqual
    "OTPs are the same"
    (display ownTotp.code)
    (display cryptoniteTOTP)

testCryptonSHA512TOTP :: Assertion
testCryptonSHA512TOTP = do
  let hexKey = "e90cbae2d7d187f614806347cfd75002bd0db847451109599da507e8da88bf43"
  key <- assertRight $ SHA512.authenticationKeyFromHexByteString hexKey
  let cryptonKey = Base.decodeBase16 $ Base.assertBase16 @StrictByteString hexKey
  timestamp@(Time ns) <- Chronos.now
  let seconds = ns `quot` (10 ^ (9 :: Int64))
  let timeStep = scale 30 Chronos.second
  digits <- assertJust $ mkDigits 6
  totpParams <-
    assertRight $
      Crypton.mkTOTPParams
        Crypton.SHA512
        0
        30
        Crypton.OTP6
        Crypton.TwoSteps

  let cryptoniteTOTP =
        Crypton.totp
          totpParams
          cryptonKey
          (fromIntegral @Int64 @Word64 seconds)

  let ownTotp = TOTP.totpSHA512 key timestamp timeStep digits

  assertEqual
    "OTPs are the same"
    (display ownTotp.code)
    (display cryptoniteTOTP)