{-# 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 -- -- 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