secp256k1-0.1.0: src/Crypto/Secp256k1.hs
{-|
Module : Crypto.Secp256k1
Description : Public SECP256K1 cryptographic functions
License : PublicDomain
Maintainer : root@haskoin.com
Stability : experimental
Portability : POSIX
This module exposes crytpographic functions from Bitcoin’s secp256k1 library.
Depends on <https://github.com/bitcoin/secp256k1 secp256k1>.
-}
module Crypto.Secp256k1
( -- * Messages
Msg, msg, getMsg
-- * Secret Keys
, SecKey, secKey, getSecKey, importSecKey, exportSecKey, pubKey
-- * Public Keys
, PubKey, importPubKey, exportPubKey
-- * Signatures
, Sig, importSig, exportSig
, signMsg, verifySig
-- * Addition & Multiplication
, Tweak, tweak, getTweak
, tweakAddSecKey, tweakMulSecKey
, tweakAddPubKey, tweakMulPubKey
, combinePubKeys
) where
import Control.Monad.Reader
import Crypto.Secp256k1.Internal
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Foreign
import System.IO.Unsafe
-- | Internal public key data type.
newtype PubKey = PubKey (ForeignPtr PubKey64)
-- | Internal message data type.
newtype Msg = Msg (ForeignPtr Msg32)
-- | Internal signature data type.
newtype Sig = Sig (ForeignPtr Sig64)
-- | Internal secret key data type.
newtype SecKey = SecKey (ForeignPtr SecKey32)
-- | Internal tweak data type for addition and multiplication.
newtype Tweak = Tweak (ForeignPtr Tweak32)
instance Show PubKey where
show = show . exportPubKey True
instance Show Msg where
show = show . getMsg
instance Show Sig where
show = show . exportSig
instance Show SecKey where
show = show . getSecKey
instance Show Tweak where
show = show . getTweak
instance Eq PubKey where
fp1 == fp2 = getPubKey fp1 == getPubKey fp2
instance Eq Msg where
fm1 == fm2 = getMsg fm1 == getMsg fm2
instance Eq Sig where
fg1 == fg2 = getSig fg1 == getSig fg2
instance Eq SecKey where
fk1 == fk2 = getSecKey fk1 == getSecKey fk2
instance Eq Tweak where
ft1 == ft2 = getTweak ft1 == getTweak ft2
-- | Create internal message data from 32-byte 'ByteString'.
msg :: ByteString -> Maybe Msg
msg bs
| BS.length bs == 32 = unsafePerformIO $ do
fp <- mallocForeignPtr
withForeignPtr fp $ flip poke (Msg32 bs)
return $ Just $ Msg fp
| otherwise = Nothing
-- | Create internal secret key data from 32-byte 'ByteString'.
secKey :: ByteString -> Maybe SecKey
secKey bs
| BS.length bs == 32 = unsafePerformIO $ do
fp <- mallocForeignPtr
ret <- withForeignPtr fp $ \p -> do
poke p (SecKey32 bs)
ec_seckey_verify ctx p
if isSuccess ret
then return $ Just $ SecKey fp
else return $ Nothing
| otherwise = Nothing
-- | Create internal tweak data from 32-byte 'ByteString'.
tweak :: ByteString -> Maybe Tweak
tweak bs
| BS.length bs == 32 = unsafePerformIO $ do
fp <- mallocForeignPtr
withForeignPtr fp $ flip poke (Tweak32 bs)
return $ Just $ Tweak fp
| otherwise = Nothing
-- | Get 32-byte secret key.
getSecKey :: SecKey -> ByteString
getSecKey (SecKey fk) = getSecKey32 $ unsafePerformIO $ withForeignPtr fk peek
getPubKey :: PubKey -> ByteString
getPubKey (PubKey fp) = getPubKey64 $ unsafePerformIO $ withForeignPtr fp peek
getSig :: Sig -> ByteString
getSig (Sig fg) = getSig64 $ unsafePerformIO $ withForeignPtr fg peek
-- | Get 32-byte message.
getMsg :: Msg -> ByteString
getMsg (Msg fm) = getMsg32 $ unsafePerformIO $ withForeignPtr fm $ peek
-- | Get 32-byte tweak.
getTweak :: Tweak -> ByteString
getTweak (Tweak ft) = getTweak32 $ unsafePerformIO $ withForeignPtr ft $ peek
-- | Read DER-encoded public key.
importPubKey :: ByteString -> Maybe PubKey
importPubKey bs = unsafePerformIO $ do
useByteString bs $ \(b, l) -> do
fp <- mallocForeignPtr
ret <- withForeignPtr fp $ \p -> ec_pubkey_parse ctx p b l
if isSuccess ret then return $ Just $ PubKey fp else return Nothing
-- | Encode public key as DER. First argument 'True' for compressed output.
exportPubKey :: Bool -> PubKey -> ByteString
exportPubKey compress (PubKey pub) = unsafePerformIO $
withForeignPtr pub $ \p -> alloca $ \l -> allocaBytes 65 $ \o -> do
poke l 65
ret <- ec_pubkey_serialize ctx o l p c
unless (isSuccess ret) $ error "could not serialize public key"
n <- peek l
packByteString (o, n)
where
c = if compress then compressed else uncompressed
-- | Read DER-encoded signature.
importSig :: ByteString -> Maybe Sig
importSig bs = unsafePerformIO $
useByteString bs $ \(b, l) -> do
fg <- mallocForeignPtr
ret <- withForeignPtr fg $ \g -> ecdsa_signature_parse_der ctx g b l
if isSuccess ret then return $ Just $ Sig fg else return Nothing
-- | Encode signature as DER.
exportSig :: Sig -> ByteString
exportSig (Sig fg) = unsafePerformIO $
withForeignPtr fg $ \g -> alloca $ \l -> allocaBytes 72 $ \o -> do
poke l 72
ret <- ecdsa_signature_serialize_der ctx o l g
unless (isSuccess ret) $ error "could not serialize signature"
n <- peek l
packByteString (o, n)
-- | Verify message signature. 'True' means that the signature is correct.
verifySig :: PubKey -> Sig -> Msg -> Bool
verifySig (PubKey fp) (Sig fg) (Msg fm) = unsafePerformIO $
withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->
withForeignPtr fm $ \m -> isSuccess <$> ecdsa_verify ctx g m p
-- | Sign message using secret key.
signMsg :: SecKey -> Msg -> Sig
signMsg (SecKey fk) (Msg fm) = unsafePerformIO $
withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do
fg <- mallocForeignPtr
ret <- withForeignPtr fg $ \g -> ecdsa_sign ctx g m k nullFunPtr nullPtr
unless (isSuccess ret) $ error "could not sign message"
return $ Sig fg
-- | Obtain public key from secret key.
pubKey :: SecKey -> PubKey
pubKey (SecKey fk) = unsafePerformIO $
withForeignPtr fk $ \k -> do
fp <- mallocForeignPtr
ret <- withForeignPtr fp $ \p -> ec_pubkey_create ctx p k
unless (isSuccess ret) $ error "could not compute public key"
return $ PubKey fp
-- | Read BER-encoded secret key.
importSecKey :: ByteString -> Maybe SecKey
importSecKey bs = unsafePerformIO $
useByteString bs $ \(b, l) -> do
fk <- mallocForeignPtr
ret <- withForeignPtr fk $ \k -> ec_privkey_import ctx k b l
if isSuccess ret then return $ Just $ SecKey fk else return Nothing
-- | Encode secret key as BER. First argument 'True' for compressed output.
exportSecKey :: Bool -> SecKey -> ByteString
exportSecKey compress (SecKey fk) = unsafePerformIO $
withForeignPtr fk $ \k -> alloca $ \l -> allocaBytes 279 $ \o -> do
poke l 279
ret <- ec_privkey_export ctx o l k c
unless (isSuccess ret) $ error "could not export secret key"
n <- peek l
packByteString (o, n)
where
c = if compress then compressed else uncompressed
-- | Add tweak to secret key using ECDSA addition.
tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey
tweakAddSecKey (SecKey fk) (Tweak ft) = unsafePerformIO $
withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do
fk' <- mallocForeignPtr
ret <- withForeignPtr fk' $ \k' -> do
key <- peek k
poke k' key
ec_privkey_tweak_add ctx k' t
if isSuccess ret then return $ Just $ SecKey fk' else return Nothing
-- | Multiply secret key by tweak using ECDSA multiplication.
tweakMulSecKey :: SecKey -> Tweak -> Maybe SecKey
tweakMulSecKey (SecKey fk) (Tweak ft) = unsafePerformIO $
withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do
fk' <- mallocForeignPtr
ret <- withForeignPtr fk' $ \k' -> do
key <- peek k
poke k' key
ec_privkey_tweak_mul ctx k' t
if isSuccess ret then return $ Just $ SecKey fk' else return Nothing
-- | Perform ECDSA addition between the public key point and the point obtained
-- by multiplying the tweak scalar by the curve generator.
tweakAddPubKey :: PubKey -> Tweak -> Maybe PubKey
tweakAddPubKey (PubKey fp) (Tweak ft) = unsafePerformIO $
withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do
fp' <- mallocForeignPtr
ret <- withForeignPtr fp' $ \p' -> do
pub <- peek p
poke p' pub
ec_pubkey_tweak_add ctx p' t
if isSuccess ret then return $ Just $ PubKey fp' else return Nothing
-- | Perform ECDSA multiplication between the public key point and the point
-- obtained by multiplying the tweak scalar by the curve generator.
tweakMulPubKey :: PubKey -> Tweak -> Maybe PubKey
tweakMulPubKey (PubKey fp) (Tweak ft) = unsafePerformIO $
withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do
fp' <- mallocForeignPtr
ret <- withForeignPtr fp' $ \p' -> do
pub <- peek p
poke p' pub
ec_pubkey_tweak_mul ctx p' t
if isSuccess ret then return $ Just $ PubKey fp' else return Nothing
-- | Add multiple public keys together using ECDSA addition.
combinePubKeys :: [PubKey] -> Maybe PubKey
combinePubKeys pubs = unsafePerformIO $ pointers [] pubs $ \ps ->
allocaArray (length ps) $ \a -> do
pokeArray a ps
fp <- mallocForeignPtr
ret <- withForeignPtr fp $ \p ->
ec_pubkey_combine ctx p a (fromIntegral $ length ps)
if isSuccess ret
then return $ Just $ PubKey fp
else return Nothing
where
pointers ps [] f = f ps
pointers ps (PubKey fp : pubs') f =
withForeignPtr fp $ \p -> pointers (p:ps) pubs' f