{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.Haskoin.Keys.Common
(
PubKeyI(..)
, SecKeyI(..)
, exportPubKey
, importPubKey
, wrapPubKey
, derivePubKeyI
, wrapSecKey
, fromMiniKey
, tweakPubKey
, tweakSecKey
, secKeyPut
, secKeyGet
, getSecKey
, secKey
) where
import Control.Applicative ((<|>))
import Control.DeepSeq (NFData, rnf)
import Control.Monad (guard, mzero, (<=<))
import Crypto.Secp256k1
import Data.Aeson (FromJSON, ToJSON, Value (String),
parseJSON, toJSON, withText)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Maybe (fromMaybe)
import Data.Serialize (Serialize, decode, encode, get,
put)
import Data.Serialize.Get (Get, getByteString)
import Data.Serialize.Put (Putter, putByteString)
import Data.String (IsString, fromString)
import Data.String.Conversions (cs)
import Network.Haskoin.Crypto.Hash
import Network.Haskoin.Util
import Text.Read (lexP, parens, pfail, readPrec)
import qualified Text.Read as Read
data PubKeyI = PubKeyI
{ pubKeyPoint :: !PubKey
, pubKeyCompressed :: !Bool
} deriving (Eq)
instance Show PubKeyI where
showsPrec _ = shows . encodeHex . encode
instance Read PubKeyI where
readPrec = parens $ do
Read.String str <- lexP
maybe pfail return $ eitherToMaybe . decode <=< decodeHex $ cs str
instance IsString PubKeyI where
fromString str =
fromMaybe e $ eitherToMaybe . decode <=< decodeHex $ cs str
where
e = error "Could not decode public key"
instance NFData PubKeyI where
rnf (PubKeyI p c) = p `seq` rnf c `seq` rnf ()
instance ToJSON PubKeyI where
toJSON = String . encodeHex . encode
instance FromJSON PubKeyI where
parseJSON = withText "PubKeyI" $
maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex
instance Serialize PubKeyI where
get = c <|> u
where
c = do
bs <- getByteString 33
guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]
maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True
u = do
bs <- getByteString 65
guard $ BS.head bs == 0x04
maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False
put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)
wrapPubKey :: Bool -> PubKey -> PubKeyI
wrapPubKey c p = PubKeyI p c
derivePubKeyI :: SecKeyI -> PubKeyI
derivePubKeyI (SecKeyI d c) = PubKeyI (derivePubKey d) c
tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey
tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)
data SecKeyI = SecKeyI
{ secKeyData :: !SecKey
, secKeyCompressed :: !Bool
} deriving (Eq, Show, Read)
instance NFData SecKeyI where
rnf (SecKeyI k c) = k `seq` rnf c `seq` ()
wrapSecKey :: Bool -> SecKey -> SecKeyI
wrapSecKey c d = SecKeyI d c
secKeyGet :: Get SecKey
secKeyGet = do
bs <- getByteString 32
maybe (fail "invalid private key") return (secKey bs)
secKeyPut :: Putter SecKey
secKeyPut = putByteString . getSecKey
fromMiniKey :: ByteString -> Maybe SecKeyI
fromMiniKey bs = do
guard checkShortKey
wrapSecKey False <$> secKey (encode (sha256 bs))
where
checkHash = encode $ sha256 $ bs `BS.append` "?"
checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00
tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey
tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)