module Data.Blockchain.Crypto.ECDSA
( KeyPair(..)
, generate
, Signature(..)
, PublicKey(..)
, PrivateKey(..)
, sign
, verify
) where
import qualified Crypto.Hash as Crypto
import qualified Crypto.PubKey.ECC.ECDSA as Crypto
import qualified Crypto.PubKey.ECC.Generate as Crypto
import qualified Crypto.PubKey.ECC.Types as Crypto
import qualified Data.Aeson as Aeson
import qualified Data.ByteString as BS
import qualified Data.Hashable as H
import Data.Monoid ((<>))
import qualified Data.Text as Text
import Data.Blockchain.Types.Hex
data KeyPair = KeyPair
{ publicKey :: PublicKey
, privateKey :: PrivateKey
}
newtype Signature = Signature { unSignature :: Crypto.Signature }
deriving (Eq)
instance Show Signature where
show (Signature (Crypto.Signature r s)) = show (hex256FromInteger r) <> show (hex256FromInteger s)
instance Aeson.ToJSON Signature where
toJSON = Aeson.toJSON . show
instance Aeson.FromJSON Signature where
parseJSON = Aeson.withText "Signature" $ \txt -> do
(rHex, sHex) <- parseHex256Tuple txt
let sig = Crypto.Signature (toInteger rHex) (toInteger sHex)
return (Signature sig)
newtype PublicKey = PublicKey { unPublicKey :: Crypto.PublicPoint }
deriving (Eq)
instance H.Hashable PublicKey where
hashWithSalt _ = H.hash . show
instance Show PublicKey where
show = \case PublicKey Crypto.PointO -> show $ PublicKey (Crypto.Point 0 0)
PublicKey (Crypto.Point x y) -> show (hex256FromInteger x) <> show (hex256FromInteger y)
instance Aeson.ToJSON PublicKey where
toJSON = Aeson.toJSON . show
instance Aeson.FromJSON PublicKey where
parseJSON = Aeson.withText "PublicKey" $ \txt -> do
(xHex, yHex) <- parseHex256Tuple txt
let point = Crypto.Point (toInteger xHex) (toInteger yHex)
return (PublicKey point)
newtype PrivateKey = PrivateKey { unPrivateKey :: Crypto.PrivateNumber }
deriving (Eq)
instance Show PrivateKey where
show = show . hex256FromInteger . unPrivateKey
instance Aeson.ToJSON PrivateKey where
toJSON = Aeson.toJSON . show
instance Aeson.FromJSON PrivateKey where
parseJSON = Aeson.withText "PrivateKey" $ fmap (PrivateKey . toInteger) . parseHex256
hashType :: Crypto.SHA256
hashType = Crypto.SHA256
curve :: Crypto.Curve
curve = Crypto.getCurveByName Crypto.SEC_p256k1
sign :: PrivateKey -> BS.ByteString -> IO Signature
sign (PrivateKey number) = fmap Signature . Crypto.sign privKey hashType
where
privKey = Crypto.PrivateKey curve number
verify :: PublicKey -> Signature -> BS.ByteString -> Bool
verify (PublicKey point) (Signature sig) = Crypto.verify hashType pubKey sig
where
pubKey = Crypto.PublicKey curve point
generate :: IO KeyPair
generate = do
(pub, priv) <- Crypto.generate curve
let publicPoint = Crypto.public_q pub
privateNumber = Crypto.private_d priv
return $ KeyPair (PublicKey publicPoint) (PrivateKey privateNumber)
parseHex256 :: Monad m => Text.Text -> m Hex256
parseHex256 = maybe (fail "Invalid hex 256 string") return . hex256 . Text.unpack
parseHex256Tuple :: Monad m => Text.Text -> m (Hex256, Hex256)
parseHex256Tuple txt = do
let (xStr, yStr) = Text.splitAt 64 txt
x <- parseHex256 xStr
y <- parseHex256 yStr
return (x, y)
hex256FromInteger :: Integer -> Hex256
hex256FromInteger = fromInteger