packages feed

ethereum-client-haskell-0.0.2: src/Blockchain/ExtendedECDSA.hs

module Blockchain.ExtendedECDSA (
  ExtendedSignature(..),
  extSignMsg,
  getPubKeyFromSignature 
  ) where

import Control.Monad
import qualified Control.Monad.State as S
import Control.Monad.Trans (lift)
import Data.Bits

import Network.Haskoin.Constants
import Network.Haskoin.Crypto
import Network.Haskoin.Internals
import Network.Haskoin.Util

--import Debug.Trace

nextSecret :: Monad m => SecretT m FieldN
nextSecret = do
    (ws,f) <- S.get
    let (ws',randM) = hmacDRBGGen ws 32 (stringToBS haskoinUserAgent)
    case randM of
        (Just rand) -> do
            S.put (ws',f)
            let randI = bsToInteger rand
            if isIntegerValidKey randI
                then return $ fromInteger randI
                else nextSecret
        Nothing -> do
            seed <- lift $ f 32 -- Read 256 bits to re-seed the PRNG
            let ws0 = hmacDRBGRsd ws' seed (stringToBS haskoinUserAgent)
            S.put (ws0,f)
            nextSecret

genKeyPair :: Monad m => SecretT m (FieldN, Point)
genKeyPair = do
    -- 3.2.1.1 
    d <- nextSecret
    -- 3.2.1.2
    let q = mulPoint d curveG
    -- 3.2.1.3
    return (d,q)

-----------------------

data ExtendedSignature = ExtendedSignature Signature Bool deriving (Show, Eq)

unsafeExtSignMsg :: Word256 -> FieldN -> (FieldN, Point) -> Maybe ExtendedSignature
unsafeExtSignMsg _ 0 _ = Nothing
unsafeExtSignMsg h d (k,p) = do
    -- 4.1.3.1 (4.1.3.2 not required)
    (x,y) <- getAffine p
    -- 4.1.3.3
    let r = fromIntegral x :: FieldN
    guard (r /= 0)
    -- 4.1.3.4 / 4.1.3.5
    let e = fromIntegral h :: FieldN
    -- 4.1.3.6
    let s' = (e + r*d)/k
        -- Canonicalize signatures: s <= order/2
        -- maxBound/2 = (maxBound+1)/2 = order/2 (because order is odd)
        s  = if s' > (maxBound `div` 2) then (-s') else s'
    guard (s /= 0)
    -- 4.1.3.7
    --return $ (Signature r s, odd y `xor` (s' > (maxBound `div` 2)))
    return $ ExtendedSignature (Signature r s) (odd y `xor` (s' > (maxBound `div` 2)))

extSignMsg :: Monad m => Word256 -> PrvKey -> SecretT m ExtendedSignature
extSignMsg _ (PrvKey  0) = error "signMsg: Invalid private key 0"
extSignMsg _ (PrvKeyU 0) = error "signMsg: Invalid private key 0"
extSignMsg h d = do
    -- 4.1.3.1
    (k,p) <- genKeyPair
    case unsafeExtSignMsg h (prvKeyFieldN d) (k,p) of
        (Just sig) -> return sig
        -- If signing failed, retry with a new nonce
        Nothing    -> extSignMsg h d

-------------------

getPubKeyFromSignature::ExtendedSignature->Word256->PubKey
getPubKeyFromSignature (ExtendedSignature sig yIsOdd) msgHash = 
  PubKey $ ((s / r) `mulPoint` bigR) `addPoint` ((fromIntegral curveN - fromIntegral msgHash/r) `mulPoint` curveG)
  where
    r = sigR sig
    s = sigS sig
    ys = quadraticResidue $ fromIntegral r^(3::Integer)+7
    correctY = if odd (head ys) == yIsOdd then head ys else ys !! 1
    Just bigR = makePoint (fromIntegral r) correctY