module Network.Haskoin.Crypto.Keys
( PubKeyI(pubKeyCompressed, pubKeyPoint)
, PubKey, PubKeyC, PubKeyU
, makePubKey
, makePubKeyG
, makePubKeyC
, makePubKeyU
, toPubKeyG
, eitherPubKey
, maybePubKeyC
, maybePubKeyU
, derivePubKey
, pubKeyAddr
, tweakPubKeyC
, PrvKeyI(prvKeyCompressed, prvKeySecKey)
, PrvKey, PrvKeyC, PrvKeyU
, makePrvKey
, makePrvKeyG
, makePrvKeyC
, makePrvKeyU
, toPrvKeyG
, eitherPrvKey
, maybePrvKeyC
, maybePrvKeyU
, encodePrvKey
, decodePrvKey
, prvKeyPutMonad
, prvKeyGetMonad
, fromWif
, toWif
, tweakPrvKeyC
) where
import Control.Applicative ((<|>))
import Control.Monad ((<=<), guard, mzero)
import Control.DeepSeq (NFData, rnf)
import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText)
import Data.Maybe (fromMaybe)
import Data.Binary (Binary, get, put)
import Data.Binary.Get (Get, getByteString)
import Data.Binary.Put (Put, putByteString)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
( head, tail
, last, init
, cons, snoc
, length, elem, pack
)
import Data.String (IsString, fromString)
import Data.String.Conversions (cs)
import qualified Crypto.Secp256k1 as EC
import Text.Read (readPrec, parens, lexP, pfail)
import qualified Text.Read as Read (Lexeme(Ident, String))
import Network.Haskoin.Crypto.Base58
import Network.Haskoin.Crypto.Hash
import Network.Haskoin.Constants
import Network.Haskoin.Util
data Generic
data Compressed
data Uncompressed
type PubKey = PubKeyI Generic
type PubKeyC = PubKeyI Compressed
type PubKeyU = PubKeyI Uncompressed
data PubKeyI c = PubKeyI
{ pubKeyPoint :: !EC.PubKey
, pubKeyCompressed :: !Bool
} deriving (Eq)
instance Show PubKey where
showsPrec d k = showParen (d > 10) $
showString "PubKey " . shows (encodeHex $ encode' k)
instance Show PubKeyC where
showsPrec d k = showParen (d > 10) $
showString "PubKeyC " . shows (encodeHex $ encode' k)
instance Show PubKeyU where
showsPrec d k = showParen (d > 10) $
showString "PubKeyU " . shows (encodeHex $ encode' k)
instance Read PubKey where
readPrec = parens $ do
Read.Ident "PubKey" <- lexP
Read.String str <- lexP
maybe pfail return $ decodeToMaybe <=< decodeHex $ cs str
instance Read PubKeyC where
readPrec = parens $ do
Read.Ident "PubKeyC" <- lexP
Read.String str <- lexP
maybe pfail return $ decodeToMaybe <=< decodeHex $ cs str
instance Read PubKeyU where
readPrec = parens $ do
Read.Ident "PubKeyU" <- lexP
Read.String str <- lexP
maybe pfail return $ decodeToMaybe <=< decodeHex $ cs str
instance IsString PubKey where
fromString str =
fromMaybe e $ decodeToMaybe <=< decodeHex $ cs str
where
e = error "Could not decode public key"
instance IsString PubKeyC where
fromString str =
fromMaybe e $ decodeToMaybe <=< decodeHex $ cs str
where
e = error "Could not decode compressed public key"
instance IsString PubKeyU where
fromString str =
fromMaybe e $ decodeToMaybe <=< decodeHex $ cs str
where
e = error "Could not decode uncompressed public key"
instance NFData (PubKeyI c) where
rnf (PubKeyI p c) = p `seq` rnf c
instance ToJSON PubKey where
toJSON = String . cs . encodeHex . encode'
instance FromJSON PubKey where
parseJSON = withText "PubKey" $
maybe mzero return . (decodeToMaybe =<<) . decodeHex . cs
instance ToJSON PubKeyC where
toJSON = String . cs . encodeHex . encode'
instance FromJSON PubKeyC where
parseJSON = withText "PubKeyC" $
maybe mzero return . (decodeToMaybe =<<) . decodeHex . cs
instance ToJSON PubKeyU where
toJSON = String . cs . encodeHex . encode'
instance FromJSON PubKeyU where
parseJSON = withText "PubKeyU" $
maybe mzero return . (decodeToMaybe =<<) . decodeHex . cs
makePubKey :: EC.PubKey -> PubKey
makePubKey p = PubKeyI p True
makePubKeyG :: Bool -> EC.PubKey -> PubKey
makePubKeyG c p = PubKeyI p c
makePubKeyC :: EC.PubKey -> PubKeyC
makePubKeyC p = PubKeyI p True
makePubKeyU :: EC.PubKey -> PubKeyU
makePubKeyU p = PubKeyI p False
toPubKeyG :: PubKeyI c -> PubKey
toPubKeyG (PubKeyI p c) = makePubKeyG c p
eitherPubKey :: PubKeyI c -> Either PubKeyU PubKeyC
eitherPubKey pk
| pubKeyCompressed pk = Right $ makePubKeyC $ pubKeyPoint pk
| otherwise = Left $ makePubKeyU $ pubKeyPoint pk
maybePubKeyC :: PubKeyI c -> Maybe PubKeyC
maybePubKeyC pk
| pubKeyCompressed pk = Just $ makePubKeyC $ pubKeyPoint pk
| otherwise = Nothing
maybePubKeyU :: PubKeyI c -> Maybe PubKeyU
maybePubKeyU pk
| not (pubKeyCompressed pk) = Just $ makePubKeyU $ pubKeyPoint pk
| otherwise = Nothing
derivePubKey :: PrvKeyI c -> PubKeyI c
derivePubKey (PrvKeyI d c) = PubKeyI (EC.derivePubKey d) c
instance Binary PubKey where
get =
(toPubKeyG <$> getC) <|> (toPubKeyG <$> getU)
where
getC = get :: Get (PubKeyI Compressed)
getU = get :: Get (PubKeyI Uncompressed)
put pk = case eitherPubKey pk of
Left k -> put k
Right k -> put k
instance Binary PubKeyC where
get = do
bs <- getByteString 33
guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]
maybe mzero return $ makePubKeyC <$> EC.importPubKey bs
put pk = putByteString $ EC.exportPubKey True $ pubKeyPoint pk
instance Binary PubKeyU where
get = do
bs <- getByteString 65
guard $ BS.head bs == 0x04
maybe mzero return $ makePubKeyU <$> EC.importPubKey bs
put pk = putByteString $ EC.exportPubKey False $ pubKeyPoint pk
pubKeyAddr :: Binary (PubKeyI c) => PubKeyI c -> Address
pubKeyAddr = PubKeyAddress . hash160 . getHash256 . hash256 . encode'
tweakPubKeyC :: PubKeyC -> Hash256 -> Maybe PubKeyC
tweakPubKeyC pub h =
makePubKeyC <$> (EC.tweakAddPubKey point =<< tweak)
where
point = pubKeyPoint pub
tweak = EC.tweak $ getHash256 h
data PrvKeyI c = PrvKeyI
{ prvKeySecKey :: !EC.SecKey
, prvKeyCompressed :: !Bool
} deriving (Eq)
instance NFData (PrvKeyI c) where
rnf (PrvKeyI s b) = s `seq` b `seq` ()
instance Show PrvKey where
showsPrec d k = showParen (d > 10) $
showString "PrvKey " . shows (toWif k)
instance Show PrvKeyC where
showsPrec d k = showParen (d > 10) $
showString "PrvKeyC " . shows (toWif k)
instance Show PrvKeyU where
showsPrec d k = showParen (d > 10) $
showString "PrvKeyU " . shows (toWif k)
instance Read PrvKey where
readPrec = parens $ do
Read.Ident "PrvKey" <- lexP
Read.String str <- lexP
maybe pfail return $ fromWif $ cs str
instance Read PrvKeyC where
readPrec = parens $ do
Read.Ident "PrvKeyC" <- lexP
Read.String str <- lexP
key <- maybe pfail return $ fromWif $ cs str
case eitherPrvKey key of
Left _ -> pfail
Right k -> return k
instance Read PrvKeyU where
readPrec = parens $ do
Read.Ident "PrvKeyU" <- lexP
Read.String str <- lexP
key <- maybe pfail return $ fromWif $ cs str
case eitherPrvKey key of
Left k -> return k
Right _ -> pfail
instance IsString PrvKey where
fromString str =
fromMaybe e $ fromWif $ cs str
where
e = error "Could not decode WIF"
instance IsString PrvKeyC where
fromString str =
case eitherPrvKey key of
Left _ -> undefined
Right k -> k
where
key = fromMaybe e $ fromWif $ cs str
e = error "Could not decode WIF"
instance IsString PrvKeyU where
fromString str =
case eitherPrvKey key of
Left k -> k
Right _ -> undefined
where
key = fromMaybe e $ fromWif $ cs str
e = error "Could not decode WIF"
type PrvKey = PrvKeyI Generic
type PrvKeyC = PrvKeyI Compressed
type PrvKeyU = PrvKeyI Uncompressed
makePrvKeyI :: Bool -> EC.SecKey -> PrvKeyI c
makePrvKeyI c d = PrvKeyI d c
makePrvKey :: EC.SecKey -> PrvKey
makePrvKey d = makePrvKeyI True d
makePrvKeyG :: Bool -> EC.SecKey -> PrvKey
makePrvKeyG = makePrvKeyI
makePrvKeyC :: EC.SecKey -> PrvKeyC
makePrvKeyC d = makePrvKeyI True d
makePrvKeyU :: EC.SecKey -> PrvKeyU
makePrvKeyU d = makePrvKeyI False d
toPrvKeyG :: PrvKeyI c -> PrvKey
toPrvKeyG (PrvKeyI d c) = PrvKeyI d c
eitherPrvKey :: PrvKeyI c -> Either PrvKeyU PrvKeyC
eitherPrvKey (PrvKeyI d compressed)
| compressed = Right $ PrvKeyI d compressed
| otherwise = Left $ PrvKeyI d compressed
maybePrvKeyC :: PrvKeyI c -> Maybe PrvKeyC
maybePrvKeyC (PrvKeyI d compressed)
| compressed = Just $ PrvKeyI d compressed
| otherwise = Nothing
maybePrvKeyU :: PrvKeyI c -> Maybe PrvKeyU
maybePrvKeyU (PrvKeyI d compressed)
| not compressed = Just $ PrvKeyI d compressed
| otherwise = Nothing
encodePrvKey :: PrvKeyI c -> ByteString
encodePrvKey (PrvKeyI d _) = EC.getSecKey d
decodePrvKey :: (EC.SecKey -> PrvKeyI c) -> ByteString -> Maybe (PrvKeyI c)
decodePrvKey f bs = f <$> EC.secKey bs
prvKeyGetMonad :: (EC.SecKey -> PrvKeyI c) -> Get (PrvKeyI c)
prvKeyGetMonad f = do
bs <- getByteString 32
fromMaybe err $ return <$> f <$> EC.secKey bs
where
err = fail "Get: Invalid private key"
prvKeyPutMonad :: PrvKeyI c -> Put
prvKeyPutMonad (PrvKeyI k _) = putByteString $ EC.getSecKey k
fromWif :: ByteString -> Maybe PrvKey
fromWif wif = do
bs <- decodeBase58Check wif
guard (BS.head bs == secretPrefix)
case BS.length bs of
33 -> do
makePrvKeyG False <$> EC.secKey (BS.tail bs)
34 -> do
guard $ BS.last bs == 0x01
makePrvKeyG True <$> EC.secKey (BS.tail $ BS.init bs)
_ -> Nothing
toWif :: PrvKeyI c -> ByteString
toWif (PrvKeyI k c) = encodeBase58Check $ BS.cons secretPrefix $
if c then EC.getSecKey k `BS.snoc` 0x01 else EC.getSecKey k
tweakPrvKeyC :: PrvKeyC -> Hash256 -> Maybe PrvKeyC
tweakPrvKeyC key h =
makePrvKeyC <$> (EC.tweakAddSecKey sec =<< tweak)
where
sec = prvKeySecKey key
tweak = EC.tweak $ getHash256 h