{-# LANGUAGE OverloadedStrings #-}
module Network.Haskoin.Crypto.Signature
( putSig
, getSig
, signHash
, verifyHashSig
, isCanonicalHalfOrder
, decodeStrictSig
, exportSig
) where
import Control.Monad (guard, unless, when)
import Crypto.Secp256k1
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.ByteString.Short (toShort)
import Data.Maybe (fromMaybe)
import Data.Serialize as S
import Data.Serialize.Put (Putter, putByteString)
import Network.Haskoin.Crypto.Hash
import Numeric (showHex)
hashToMsg :: Hash256 -> Msg
hashToMsg =
fromMaybe e . msg . encode
where
e = error "Could not convert 32-byte hash to secp256k1 message"
signHash :: SecKey -> Hash256 -> Sig
signHash k = signMsg k . hashToMsg
verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool
verifyHashSig h s p =
verifySig p g m
where
(g, _) = normalizeSig s
m = hashToMsg h
getSig :: Get Sig
getSig = do
l <- lookAhead $ do
t <- getWord8
unless (t == 0x30) $ fail $
"Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"
l <- getWord8
when (l == 0x00) $ fail "Indeterminate form unsupported"
when (l >= 0x80) $ fail "Multi-octect length not supported"
return $ fromIntegral l
bs <- getByteString $ l + 2
case decodeStrictSig bs of
Just s -> return s
Nothing -> fail "Invalid signature"
putSig :: Putter Sig
putSig s = putByteString $ exportSig s
isCanonicalHalfOrder :: Sig -> Bool
isCanonicalHalfOrder = not . snd . normalizeSig
decodeStrictSig :: ByteString -> Maybe Sig
decodeStrictSig bs = do
g <- importSig bs
let compact = exportCompactSig g
guard $ getCompactSigR compact /= zero
guard $ getCompactSigS compact /= zero
guard $ isCanonicalHalfOrder g
return g
where
zero = toShort $ BS.replicate 32 0