packages feed

haskoin-crypto-0.0.1: tests/Network/Haskoin/Crypto/ECDSA/Tests.hs

module Network.Haskoin.Crypto.ECDSA.Tests (tests) where

import Test.Framework (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck.Property ((==>), Property)

import Data.Bits (testBit)
import qualified Data.ByteString as BS

import QuickCheckUtils
import Network.Haskoin.Crypto.Arbitrary()

import Network.Haskoin.Crypto.ECDSA
import Network.Haskoin.Crypto.Point
import Network.Haskoin.Crypto.Ring
import Network.Haskoin.Crypto.Keys
import Network.Haskoin.Crypto.Curve
import Network.Haskoin.Util

tests :: [Test]
tests = 
    [ testGroup "ECDSA signatures"
        [ testProperty "verify( sign(msg) ) = True" signAndVerify
        , testProperty "verify( detSign(msg) ) = True" signAndVerifyD
        , testProperty "S component <= order/2" halfOrderSig
        ],
      testGroup "ECDSA Binary"
        [ testProperty "get( put(Sig) ) = Sig" getPutSig
        , testProperty "Encoded signature is canonical" testIsCanonical
        ]
    ]

{- ECDSA Signatures -}

signAndVerify :: Hash256 -> FieldN -> FieldN -> Property
signAndVerify msg k n = k > 0 && n > 0 ==> case sM of
    (Just s) -> verifySig msg s (PubKey kP)
    Nothing  -> True -- very bad luck
    where kP = mulPoint k curveG
          nP = mulPoint n curveG
          sM = unsafeSignMsg msg k (n,nP)

signAndVerifyD :: Hash256 -> TestPrvKeyC -> Bool
signAndVerifyD msg (TestPrvKeyC k) = verifySig msg (detSignMsg msg k) p
    where p = derivePubKey k
           
halfOrderSig :: Signature -> Bool
halfOrderSig sig@(Signature _ (Ring s)) = 
    s <= (curveN `div` 2) && isCanonicalHalfOrder sig

{- ECDSA Binary -}

getPutSig :: Signature -> Bool
getPutSig sig = (decode' $ encode' sig) == sig

-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp
-- from function IsCanonicalSignature
testIsCanonical :: Signature -> Bool
testIsCanonical sig = not $
    -- Non-canonical signature: too short
    (len < 8) ||
    -- Non-canonical signature: too long
    (len > 72) ||
    -- Non-canonical signature: wrong type
    (BS.index s 0 /= 0x30) ||
    -- Non-canonical signature: wrong length marker
    (BS.index s 1 /= len - 2) ||
    -- Non-canonical signature: S length misplaced
    (5 + rlen >= len) || 
    -- Non-canonical signature: R+S length mismatch
    (rlen + slen + 6 /= len) ||
    -- Non-canonical signature: R value type mismatch
    (BS.index s 2 /= 0x02) ||
    -- Non-canonical signature: R length is zero
    (rlen == 0) ||
    -- Non-canonical signature: R value negative
    (testBit (BS.index s 4) 7) ||
    -- Non-canonical signature: R value excessively padded
    (  rlen > 1 
    && BS.index s 4 == 0 
    && not (testBit (BS.index s 5) 7)
    ) ||
    -- Non-canonical signature: S value type mismatch
    (BS.index s (fromIntegral rlen+4) /= 0x02) ||
    -- Non-canonical signature: S length is zero
    (slen == 0) ||
    -- Non-canonical signature: S value negative
    (testBit (BS.index s (fromIntegral rlen+6)) 7) ||
    -- Non-canonical signature: S value excessively padded
    (  slen > 1
    && BS.index s (fromIntegral rlen+6) == 0 
    && not (testBit (BS.index s (fromIntegral rlen+7)) 7)
    ) 
    where s = encode' sig
          len = fromIntegral $ BS.length s
          rlen = BS.index s 3
          slen = BS.index s (fromIntegral rlen + 5)