{-| 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 . -} 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