module Network.EasyBitcoin.Internal.Signatures
( detSignMsg
, Signature()
, checkSig
)where
import Network.EasyBitcoin.Keys
import Network.EasyBitcoin.Internal.Keys
import Network.EasyBitcoin.Internal.Words
import Network.EasyBitcoin.Internal.CurveConstants
import Network.EasyBitcoin.Internal.ByteString
import Network.EasyBitcoin.Internal.Words
import Network.EasyBitcoin.Internal.HashFunctions
import qualified Data.ByteString as BS
import Data.Binary (Binary, get, put, Word64,Word32,Word16)
import Data.Binary.Get ( getWord64be
, getWord32be
, getWord64le
, getWord8
, getWord16le
, getWord32le
, getByteString
, Get
)
import Data.Binary.Put( putWord64be
, putWord32be
, putWord32le
, putWord64le
, putWord16le
, putWord8
, putByteString
)
import Control.Monad
import GHC.Word
import Control.Applicative
import Data.Bits
import Control.DeepSeq (NFData, rnf)
import Control.Monad (unless, guard)
import Data.Maybe
detSignMsg :: Word256 -> Key Private net -> Signature
detSignMsg n (ExtendedPrv _ _ _ _ (PrvKey x)) = detSignMsg_ n x
detSignMsg_ :: Word256 -> FieldN -> Signature
detSignMsg_ h d = go $ hmacDRBGNew (enc d) (encode' h) BS.empty
where
enc::FieldN -> BS.ByteString
enc x = encode' (fromIntegral x ::Word256)
go ws = case hmacDRBGGen ws 32 BS.empty of
(ws', Just k) -> let kI = bsToInteger k
p = mulPoint (fromInteger kI) curveG
sigM = unsafeSignMsg h d (fromInteger kI,p)
in if (isIntegerValidKey kI)
then fromMaybe (go ws') sigM
else go ws'
(_ , Nothing) -> error "detSignMsg: No suitable K value found"
unsafeSignMsg :: Word256 -> FieldN -> (FieldN, Point) -> Maybe Signature
unsafeSignMsg _ 0 _ = Nothing
unsafeSignMsg h d (k,p) = do let (x,_) = getAffine p
r = (fromIntegral x :: FieldN)
e = (fromIntegral h :: FieldN)
s' = (e + r*d)/k
s = if s' > (maxBound `div` 2) then (s') else s'
return $ Signature r s
checkSig :: Word256 -> Signature -> Key Public net -> Bool
checkSig h sig ( ExtendedPub _ _ _ _ key) = checkSig_ h sig key
where
checkSig_ _ (Signature 0 _) _ = False
checkSig_ _ (Signature _ 0) _ = False
checkSig_ h (Signature r s) q = case Just $ getAffine p of
Nothing -> False
Just (x,_) -> (fromIntegral x :: FieldN) == r
where
e = (fromIntegral h :: FieldN)
s' = inverseN s
u1 = e*s'
u2 = r*s'
p = shamirsTrick u1 curveG u2 (pubKeyPoint q)
data Signature = Signature { sigR :: !FieldN
, sigS :: !FieldN
} deriving (Read, Show, Eq)
instance Binary Signature where
get = do t <- getWord8
unless (t == 0x30) (fail $ "Bad DER identifier byte " ++ (show t) ++ ". Expecting 0x30")
l <- getWord8
isolate (fromIntegral l) $ Signature <$> get <*> get
put (Signature 0 _) = error "0 is an invalid r value in a Signature"
put (Signature _ 0) = error "0 is an invalid s value in a Signature"
put (Signature r s) = do putWord8 0x30
let c = runPut' $ put r >> put s
putWord8 (fromIntegral $ BS.length c)
putByteString c
shamirsTrick :: FieldN -> Point -> FieldN -> Point -> Point
shamirsTrick r1 p1 r2 p2 = addPoint (mulPoint r1 p1) (mulPoint r2 p2)
quadraticResidue :: FieldP -> [FieldP]
quadraticResidue x = guard (y^(2 :: Int) == x) >> [y, (y)]
where
q = (curveP + 1) `div` 4
y = x^q