haskoin-core-0.15.0: src/Haskoin/Crypto/Signature.hs
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Haskoin.Crypto.Signature
Copyright : No rights reserved
License : MIT
Maintainer : jprupp@protonmail.ch
Stability : experimental
Portability : POSIX
ECDSA signatures using secp256k1 curve. Uses functions from upstream secp256k1
library.
-}
module Haskoin.Crypto.Signature
( -- * Signatures
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.Maybe (fromMaybe, isNothing)
import Data.Serialize as S
import Haskoin.Crypto.Hash
import Numeric (showHex)
-- | Convert 256-bit hash into a 'Msg' for signing or verification.
hashToMsg :: Hash256 -> Msg
hashToMsg =
fromMaybe e . msg . encode
where
e = error "Could not convert 32-byte hash to secp256k1 message"
-- | Sign a 256-bit hash using secp256k1 elliptic curve.
signHash :: SecKey -> Hash256 -> Sig
signHash k = signMsg k . hashToMsg
-- | Verify an ECDSA signature for a 256-bit hash.
verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool
verifyHashSig h s p = verifySig p norm (hashToMsg h)
where
norm = fromMaybe s (normalizeSig s)
-- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.
getSig :: Get Sig
getSig = do
l <-
lookAhead $ do
t <- getWord8
-- 0x30 is DER sequence type
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"
-- | Serialize an ECDSA signature for Bitcoin use.
putSig :: Putter Sig
putSig s = putByteString $ exportSig s
-- | Is canonical half order.
isCanonicalHalfOrder :: Sig -> Bool
isCanonicalHalfOrder = isNothing . normalizeSig
-- | Decode signature strictly.
decodeStrictSig :: ByteString -> Maybe Sig
decodeStrictSig bs = do
g <- importSig bs
-- <http://www.secg.org/sec1-v2.pdf Section 4.1.4>
-- 4.1.4.1 (r and s can not be zero)
let compact = exportCompactSig g
let zero = BS.replicate 32 0
guard $ BS.take 32 (getCompactSig compact) /= zero
guard $ BS.take 32 (BS.drop 32 (getCompactSig compact)) /= zero
guard $ isCanonicalHalfOrder g
return g