Secp256k1.hs (22038B)
1 {-# LANGUAGE DeriveGeneric #-} 2 {-# LANGUAGE DerivingStrategies #-} 3 {-# LANGUAGE OverloadedStrings #-} 4 {-# LANGUAGE ViewPatterns #-} 5 6 -- | 7 -- Module: Crypto.Secp256k1 8 -- Copyright: (c) 2024 Jared Tobin 9 -- License: MIT 10 -- Maintainer: Jared Tobin <jared@ppad.tech> 11 -- 12 -- Bindings to bitcoin-core/secp256k1, a C library supporting digital 13 -- signatures and other cryptographic primitives on the secp256k1 14 -- elliptic curve. 15 -- 16 -- This library exposes a minimal subset of functionality, primarily 17 -- supporting ECDSA/Schnorr signatures and ECDH secret computation. 18 -- 19 -- We're interacting with a C library here, and we don't pretend that we 20 -- aren't. 'IO' is prevalent and unhidden, and asynchronous exceptions 21 -- (in the form of 'Secp256k1Exception') are the error-handling method 22 -- of choice. 23 24 module Crypto.Secp256k1 ( 25 Context(..) 26 , wcontext 27 , wrcontext 28 29 , Sig 30 , sign 31 , sign_schnorr 32 , verify 33 , verify_schnorr 34 , ecdh 35 36 , parse_der 37 , serialize_der 38 , parse_compact 39 , serialize_compact 40 41 , Pub 42 , derive_pub 43 , parse_pub 44 , tweak_pub_add 45 , tweak_pub_mul 46 , tweak_sec_add 47 , tweak_sec_mul 48 , serialize_pub 49 , serialize_pub_u 50 , XOnlyPub 51 , xonly 52 , parse_xonly 53 , serialize_xonly 54 , KeyPair 55 , keypair 56 , keypair_pub 57 , keypair_sec 58 59 , Secp256k1Exception(..) 60 ) where 61 62 import Control.Exception (Exception, bracket, throwIO) 63 import Control.Monad (when) 64 import Crypto.Secp256k1.Internal hiding (Context, wcontext) 65 import qualified Crypto.Secp256k1.Internal as I (Context) 66 import GHC.Generics 67 import qualified Data.ByteString as BS 68 import qualified Foreign.Marshal.Alloc as A (alloca, allocaBytes) 69 import Foreign.Ptr (Ptr) 70 import qualified Foreign.Ptr as F (castPtr, nullPtr) 71 import qualified Foreign.Storable as S (poke, peek) 72 73 -- | A bitcoin-core/secp256k1 context. 74 -- 75 -- bitcoin-core/secp256k1 computations typically require a context, 76 -- the primary purpose of which is to store randomization data as 77 -- increased protection against side-channel attacks (and the second 78 -- of which is boring pointer storage to various library callbacks). 79 -- 80 -- You should create and use values of this type via 'wrcontext' or 81 -- 'wcontext'. 82 -- 83 -- The data constructor is exported only to make the implementation 84 -- easier to benchmark. You should /not/ pattern match on or 85 -- manipulate context values. 86 newtype Context = Context (Ptr I.Context) 87 deriving stock Generic 88 89 instance Show Context where 90 show (Context tex) = "<bitcoin-core/secp256k1 context " <> show tex <> ">" 91 92 -- | A bitcoin-core/secp256k1-internal public key. 93 -- 94 -- Create a value of this type by parsing a compressed or uncompressed 95 -- public key via 'parse_pub', deriving one from a secret key via 96 -- 'create_pub', or extracting one from a keypair via 'keypair_pub'. 97 newtype Pub = Pub BS.ByteString 98 deriving stock Generic 99 100 instance Show Pub where 101 show _ = "<bitcoin-core/secp256k1 public key>" 102 103 -- | A bitcoin-core/secp256k1-internal x-only public key. 104 -- 105 -- An "x-only" public key corresponds to a public key with even 106 -- y-coordinate. 107 -- 108 -- Create a value of this type from a 'Pub' via 'xonly', or parse one 109 -- directly via 'parse_xonly'. 110 newtype XOnlyPub = XOnlyPub BS.ByteString 111 deriving stock Generic 112 113 instance Show XOnlyPub where 114 show _ = "<bitcoin-core/secp256k1 x-only public key>" 115 116 -- | A bitcoin-core/secp256k1-internal keypair. 117 -- 118 -- Create a value of this type by passing a secret key to 119 -- 'keypair'. 120 newtype KeyPair = KeyPair BS.ByteString 121 deriving stock Generic 122 123 instance Show KeyPair where 124 show _ = "<bitcoin-core/secp256k1 keypair>" 125 126 -- | A bitcoin-core/secp256k1-internal ECDSA signature. 127 -- 128 -- Create a value of this type via 'sign', or parse a DER-encoded 129 -- signature via 'parse_der'. 130 newtype Sig = Sig BS.ByteString 131 deriving stock Generic 132 133 instance Show Sig where 134 show _ = "<bitcoin-core/secp256k1 signature>" 135 136 -- exceptions 137 138 -- | A catch-all exception type. 139 -- 140 data Secp256k1Exception = 141 -- | Thrown when a bitcoin-core/secp256k1 function returns a value 142 -- indicating failure. 143 Secp256k1Error 144 -- | Thrown when a csecp256k1 function has been passed a bad (i.e., 145 -- incorrectly-sized) input. 146 | CSecp256k1Error 147 deriving Show 148 149 instance Exception Secp256k1Exception 150 151 -- context 152 153 -- | Execute the supplied continuation within a fresh 154 -- bitcoin-core/secp256k1 context. The context will be destroyed 155 -- afterwards. 156 -- 157 -- This function executes the supplied continuation in a context 158 -- that has /not/ been randomized, and so /doesn't/ offer additional 159 -- side-channel attack protection. For that, use 'wrcontext'. 160 -- 161 -- >>> wcontext $ \tex -> parse_pub tex bytestring 162 -- "<bitcoin-core/secp256k1 public key>" 163 wcontext 164 :: (Context -> IO ()) -- ^ continuation to run in the context 165 -> IO () 166 wcontext = bracket create destroy where 167 create = do 168 tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE 169 pure (Context tex) 170 171 destroy (Context tex) = 172 secp256k1_context_destroy tex 173 174 -- | Same as 'wcontext', but randomize the bitcoin-core/secp256k1 175 -- context with the provided 32 bytes of entropy before executing the 176 -- supplied continuation. 177 -- 178 -- Use this function to execute computations that may benefit from 179 -- additional side-channel attack protection. 180 -- 181 -- >>> wrcontext entropy $ \tex -> sign tex sec msg 182 -- "<bitcoin-core/secp256k1 signature>" 183 wrcontext 184 :: BS.ByteString -- ^ 32 bytes of fresh entropy 185 -> (Context -> IO ()) -- ^ continuation to run in the context 186 -> IO () 187 wrcontext enn con 188 | BS.length enn /= 32 = throwIO CSecp256k1Error 189 | otherwise = bracket create destroy con 190 where 191 create = do 192 tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE 193 BS.useAsCString enn $ \(F.castPtr -> sed) -> do 194 suc <- secp256k1_context_randomize tex sed 195 when (suc /= 1) $ throwIO Secp256k1Error 196 pure (Context tex) 197 198 destroy (Context tex) = 199 secp256k1_context_destroy tex 200 201 -- ec 202 203 -- | Derive a public key from a 32-byte secret key. 204 -- 205 -- >>> wrcontext entropy $ \tex -> derive_pub tex sec 206 -- "<bitcoin-core/secp256k1 public key>" 207 derive_pub 208 :: Context 209 -> BS.ByteString -- ^ 32-byte secret key 210 -> IO Pub 211 derive_pub (Context tex) bs 212 | BS.length bs /= 32 = throwIO CSecp256k1Error 213 | otherwise = BS.useAsCString bs $ \(F.castPtr -> sec) -> 214 A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do 215 suc <- secp256k1_ec_pubkey_create tex out sec 216 when (suc /= 1) $ throwIO Secp256k1Error 217 let pub = F.castPtr out 218 key <- BS.packCStringLen (pub, _PUB_BYTES_INTERNAL) 219 pure (Pub key) 220 221 -- | Parse a compressed (33-byte) or uncompressed (65-byte) public key. 222 -- 223 -- >>> wcontext $ \tex -> parse_pub tex bs 224 -- "<bitcoin-core/secp256k1 public key>" 225 parse_pub 226 :: Context 227 -> BS.ByteString -- ^ compressed or uncompressed public key 228 -> IO Pub 229 parse_pub (Context tex) bs = 230 BS.useAsCStringLen bs $ \(F.castPtr -> pub, fromIntegral -> len) -> 231 A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do 232 suc <- secp256k1_ec_pubkey_parse tex out pub len 233 when (suc /= 1) $ throwIO Secp256k1Error 234 let par = F.castPtr out 235 key <- BS.packCStringLen (par, _PUB_BYTES_INTERNAL) 236 pure (Pub key) 237 238 data PubFormat = 239 Compressed 240 | Uncompressed 241 242 -- | Serialize a public key into a compressed (33-byte) bytestring 243 -- representation. 244 -- 245 -- >>> wcontext $ \tex -> serialize_pub tex pub 246 serialize_pub 247 :: Context 248 -> Pub 249 -> IO BS.ByteString -- ^ serialized compressed public key 250 serialize_pub = serialize_pub_in Compressed 251 252 -- | Serialize a public key into an uncompressed (65-byte) bytestring 253 -- represention. 254 -- 255 -- >>> wcontext $ \tex -> serialize_pub_u tex pub 256 serialize_pub_u 257 :: Context 258 -> Pub 259 -> IO BS.ByteString -- ^ serialized uncompressed public key 260 serialize_pub_u = serialize_pub_in Uncompressed 261 262 serialize_pub_in :: PubFormat -> Context -> Pub -> IO BS.ByteString 263 serialize_pub_in for (Context tex) (Pub pub) = 264 BS.useAsCString pub $ \(F.castPtr -> key) -> 265 A.alloca $ \len -> 266 A.allocaBytes bys $ \out -> do 267 let siz = fromIntegral bys 268 S.poke len siz 269 suc <- secp256k1_ec_pubkey_serialize tex out len key fal 270 when (suc /= 1) $ throwIO Secp256k1Error 271 pec <- S.peek len 272 let enc = F.castPtr out 273 nel = fromIntegral pec 274 BS.packCStringLen (enc, nel) 275 where 276 bys = case for of 277 Compressed -> _PUB_BYTES_COMPRESSED 278 Uncompressed -> _PUB_BYTES_UNCOMPRESSED 279 280 fal = case for of 281 Compressed -> _COMPRESSED_FLAG 282 Uncompressed -> _UNCOMPRESSED_FLAG 283 284 -- | Additively tweak a public key with the supplied 32-byte tweak. 285 -- 286 -- >>> wrcontext $ \tex -> tweak_pub_add pub tweak 287 tweak_pub_add 288 :: Context 289 -> Pub 290 -> BS.ByteString -- ^ 32-byte tweak value 291 -> IO Pub 292 tweak_pub_add (Context tex) (Pub pub) wee 293 | BS.length wee /= 32 = throwIO CSecp256k1Error 294 | otherwise = do 295 let cop = BS.copy pub 296 BS.useAsCString cop $ \(F.castPtr -> out) -> 297 BS.useAsCString wee $ \(F.castPtr -> eek) -> do 298 suc <- secp256k1_ec_pubkey_tweak_add tex out eek 299 when (suc /= 1) $ throwIO Secp256k1Error 300 let enc = F.castPtr out 301 key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL) 302 pure (Pub key) 303 304 -- | Multiplicatively tweak a public key with the supplied 32-byte 305 -- tweak. 306 -- 307 -- >>> wrcontext $ \tex -> tweak_pub_mul pub tweak 308 tweak_pub_mul 309 :: Context 310 -> Pub 311 -> BS.ByteString -- ^ 32-byte tweak value 312 -> IO Pub 313 tweak_pub_mul (Context tex) (Pub pub) wee 314 | BS.length wee /= 32 = throwIO CSecp256k1Error 315 | otherwise = do 316 let cop = BS.copy pub 317 BS.useAsCString cop $ \(F.castPtr -> out) -> 318 BS.useAsCString wee $ \(F.castPtr -> eek) -> do 319 suc <- secp256k1_ec_pubkey_tweak_mul tex out eek 320 when (suc /= 1) $ throwIO Secp256k1Error 321 let enc = F.castPtr out 322 key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL) 323 pure (Pub key) 324 325 -- | Additively tweak a secret key with the supplied 32-byte tweak. 326 -- 327 -- >>> wrcontext $ \tex -> tweak_sec_add sec tweak 328 tweak_sec_add 329 :: Context 330 -> BS.ByteString -- ^ 32-byte secret key 331 -> BS.ByteString -- ^ 32-byte tweak value 332 -> IO BS.ByteString -- ^ 32-byte secret key 333 tweak_sec_add (Context tex) key wee 334 | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error 335 | otherwise = do 336 let sec = BS.copy key 337 BS.useAsCString sec $ \(F.castPtr -> out) -> 338 BS.useAsCString wee $ \(F.castPtr -> eek) -> do 339 suc <- secp256k1_ec_seckey_tweak_add tex out eek 340 when (suc /= 1) $ throwIO Secp256k1Error 341 let enc = F.castPtr out 342 BS.packCStringLen (enc, _SEC_BYTES) 343 344 -- | Multiplicatively tweak a secret key with the supplied 32-byte 345 -- tweak. 346 -- 347 -- >>> wrcontext $ \tex -> tweak_sec_mul sec tweak 348 tweak_sec_mul 349 :: Context 350 -> BS.ByteString -- ^ 32-byte secret key 351 -> BS.ByteString -- ^ 32-byte tweak value 352 -> IO BS.ByteString -- ^ 32-byte secret key 353 tweak_sec_mul (Context tex) key wee 354 | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error 355 | otherwise = do 356 let sec = BS.copy key 357 BS.useAsCString sec $ \(F.castPtr -> out) -> 358 BS.useAsCString wee $ \(F.castPtr -> eek) -> do 359 suc <- secp256k1_ec_seckey_tweak_mul tex out eek 360 when (suc /= 1) $ throwIO Secp256k1Error 361 let enc = F.castPtr out 362 BS.packCStringLen (enc, _SEC_BYTES) 363 364 -- ecdsa 365 366 -- | Sign a 32-byte message hash with the provided secret key. 367 -- 368 -- >>> wrcontext entropy $ \tex -> sign tex sec msg 369 -- "<bitcoin-core/secp256k1 signature>" 370 sign 371 :: Context 372 -> BS.ByteString -- ^ 32-byte secret key 373 -> BS.ByteString -- ^ 32-byte message hash 374 -> IO Sig 375 sign (Context tex) key msg 376 | BS.length key /= 32 || BS.length msg /= 32 = throwIO CSecp256k1Error 377 | otherwise = A.allocaBytes _SIG_BYTES $ \out -> 378 BS.useAsCString msg $ \(F.castPtr -> has) -> 379 BS.useAsCString key $ \(F.castPtr -> sec) -> do 380 suc <- secp256k1_ecdsa_sign tex out has sec F.nullPtr F.nullPtr 381 when (suc /= 1) $ throwIO Secp256k1Error 382 let sig = F.castPtr out 383 enc <- BS.packCStringLen (sig, _SIG_BYTES) 384 pure (Sig enc) 385 386 -- | Verify an ECDSA signature for the provided message hash with the 387 -- supplied public key. 388 -- 389 -- Returns 'True' for a verifying signature, 'False' otherwise. 390 -- 391 -- >>> wcontext $ \tex -> verify tex pub msg good_sig 392 -- True 393 -- >>> wcontext $ \tex -> verify tex pub msg bad_sig 394 -- False 395 verify 396 :: Context 397 -> Pub 398 -> BS.ByteString -- ^ 32-byte message hash 399 -> Sig 400 -> IO Bool 401 verify (Context tex) (Pub pub) msg (Sig sig) 402 | BS.length msg /= 32 = throwIO CSecp256k1Error 403 | otherwise = BS.useAsCString pub $ \(F.castPtr -> key) -> 404 BS.useAsCString sig $ \(F.castPtr -> sip) -> 405 BS.useAsCString msg $ \(F.castPtr -> has) -> do 406 suc <- secp256k1_ecdsa_verify tex sip has key 407 pure (suc == 1) 408 409 -- | Parse a DER-encoded bytestring into a signature. 410 -- 411 -- >>> wcontext $ \tex -> parse_der tex bytestring 412 -- "<bitcoin-core/secp256k1 signature>" 413 -- >>> wcontext $ \tex -> parse_der tex bad_bytestring 414 -- *** Exception: Secp256k1Error 415 parse_der 416 :: Context 417 -> BS.ByteString -- ^ DER-encoded signature 418 -> IO Sig 419 parse_der (Context tex) bs = 420 BS.useAsCStringLen bs $ \(F.castPtr -> der, fromIntegral -> len) -> 421 A.allocaBytes _SIG_BYTES $ \out -> do 422 suc <- secp256k1_ecdsa_signature_parse_der tex out der len 423 when (suc /= 1) $ throwIO Secp256k1Error 424 let par = F.castPtr out 425 sig <- BS.packCStringLen (par, _SIG_BYTES) 426 pure (Sig sig) 427 428 -- | Serialize a signature into a DER-encoded bytestring. 429 -- 430 -- >>> wcontext $ \tex -> serialize_der tex sig 431 serialize_der 432 :: Context 433 -> Sig 434 -> IO BS.ByteString -- ^ DER-encoded signature 435 serialize_der (Context tex) (Sig sig) = 436 A.alloca $ \len -> 437 A.allocaBytes _DER_BYTES $ \out -> 438 BS.useAsCString sig $ \(F.castPtr -> sip) -> do 439 let siz = fromIntegral _DER_BYTES 440 S.poke len siz 441 suc <- secp256k1_ecdsa_signature_serialize_der tex out len sip 442 when (suc /= 1) $ throwIO Secp256k1Error 443 pek <- S.peek len 444 let der = F.castPtr out 445 nel = fromIntegral pek 446 BS.packCStringLen (der, nel) 447 448 -- | Parse a bytestring encoding a compact (64-byte) signature. 449 -- 450 -- >>> wcontext $ \tex -> parse_compact tex bytestring 451 parse_compact 452 :: Context 453 -> BS.ByteString -- ^ bytestring encoding a 64-byte compact signature 454 -> IO Sig 455 parse_compact (Context tex) bs = 456 BS.useAsCString bs $ \(F.castPtr -> com) -> 457 A.allocaBytes _SIG_BYTES $ \out -> do 458 suc <- secp256k1_ecdsa_signature_parse_compact tex out com 459 when (suc /= 1) $ throwIO Secp256k1Error 460 let par = F.castPtr out 461 enc <- BS.packCStringLen (par, _SIG_BYTES) 462 pure (Sig enc) 463 464 -- | Serialize a signature into a compact (64-byte) bytestring. 465 -- 466 -- >>> wcontext $ \tex -> serialize_compact tex sig 467 serialize_compact 468 :: Context 469 -> Sig 470 -> IO BS.ByteString 471 serialize_compact (Context tex) (Sig sig) = 472 BS.useAsCString sig $ \(F.castPtr -> sip) -> 473 A.allocaBytes _SIG_BYTES $ \out -> do 474 -- always returns 1 475 _ <- secp256k1_ecdsa_signature_serialize_compact tex out sip 476 let enc = F.castPtr out 477 BS.packCStringLen (enc, _SIG_BYTES) 478 479 -- extrakeys 480 481 -- | Convert a public key into an x-only public key (i.e. one with even 482 -- y coordinate). 483 -- 484 -- >>> wcontext $ \tex -> xonly tex pub 485 -- "<bitcoin-core/secp256k1 x-only public key>" 486 xonly :: Context -> Pub -> IO XOnlyPub 487 xonly (Context tex) (Pub pub) = 488 A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> 489 BS.useAsCString pub $ \(F.castPtr -> pup) -> do 490 -- returns 1 always 491 _ <- secp256k1_xonly_pubkey_from_pubkey tex out F.nullPtr pup 492 let key = F.castPtr out 493 pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL) 494 pure (XOnlyPub pux) 495 496 -- | Parse a compressed (33-byte) or uncompressed (65-byte) public key into 497 -- an x-only public key. 498 -- 499 -- >>> wcontext $ \tex -> parse_xonly tex bytestring 500 -- "<bitcoin-core/secp256k1 x-only public key>" 501 parse_xonly 502 :: Context 503 -> BS.ByteString -- ^ compressed or uncompressed public key 504 -> IO XOnlyPub 505 parse_xonly (Context tex) bs = 506 A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> 507 BS.useAsCString bs $ \(F.castPtr -> pub) -> do 508 suc <- secp256k1_xonly_pubkey_parse tex out pub 509 when (suc /= 1) $ throwIO Secp256k1Error 510 let key = F.castPtr out 511 pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL) 512 pure (XOnlyPub pux) 513 514 -- | Serialize an x-only public key into a 32-byte bytestring 515 -- representation. 516 -- 517 -- >>> wcontext $ \tex -> serialize_xonly tex xonly 518 serialize_xonly 519 :: Context 520 -> XOnlyPub 521 -> IO BS.ByteString -- ^ serialized x-only public key 522 serialize_xonly (Context tex) (XOnlyPub pux) = 523 A.allocaBytes _PUB_BYTES_XONLY $ \out -> do 524 BS.useAsCString pux $ \(F.castPtr -> pub) -> do 525 -- returns 1 always 526 _ <- secp256k1_xonly_pubkey_serialize tex out pub 527 let enc = F.castPtr out 528 BS.packCStringLen (enc, _PUB_BYTES_XONLY) 529 530 -- | Derive a keypair from the provided 32-byte secret key. 531 -- 532 -- >>> wrcontext entropy $ \tex -> keypair tex sec 533 -- "<bitcoin-core/secp256k1 keypair>" 534 keypair 535 :: Context 536 -> BS.ByteString -- ^ 32-byte secret key 537 -> IO KeyPair 538 keypair (Context tex) sec 539 | BS.length sec /= 32 = throwIO CSecp256k1Error 540 | otherwise = A.allocaBytes _KEYPAIR_BYTES $ \out -> 541 BS.useAsCString sec $ \(F.castPtr -> key) -> do 542 suc <- secp256k1_keypair_create tex out key 543 when (suc /= 1) $ throwIO Secp256k1Error 544 let enc = F.castPtr out 545 per <- BS.packCStringLen (enc, _KEYPAIR_BYTES) 546 pure (KeyPair per) 547 548 -- | Extract a public key from a keypair. 549 -- 550 -- >>> wrcontext entropy $ \tex -> keypair_pub tex keypair 551 -- "<bitcoin-core/secp256k1 public key>" 552 keypair_pub :: Context -> KeyPair -> IO Pub 553 keypair_pub (Context tex) (KeyPair per) = 554 A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> 555 BS.useAsCString per $ \(F.castPtr -> par) -> do 556 -- returns 1 always 557 _ <- secp256k1_keypair_pub tex out par 558 let enc = F.castPtr out 559 pub <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL) 560 pure (Pub pub) 561 562 -- | Extract a secret key from a keypair. 563 -- 564 -- >>> wrcontext entropy $ \tex -> keypair_sec tex keypair 565 keypair_sec 566 :: Context 567 -> KeyPair 568 -> IO BS.ByteString -- ^ 32-byte secret key 569 keypair_sec (Context tex) (KeyPair per) = 570 A.allocaBytes _SEC_BYTES $ \out -> 571 BS.useAsCString per $ \(F.castPtr -> par) -> do 572 _ <- secp256k1_keypair_sec tex out par 573 let enc = F.castPtr out 574 BS.packCStringLen (enc, _SEC_BYTES) 575 576 -- ecdh 577 578 -- | Compute an ECDH secret key from the provided public key and 579 -- (32-byte) secret key. 580 -- 581 -- >>> wrcontext entropy $ \tex -> ecdh tex pub sec 582 ecdh 583 :: Context 584 -> Pub 585 -> BS.ByteString -- ^ 32-byte secret key 586 -> IO BS.ByteString -- ^ 32-byte secret key 587 ecdh (Context tex) (Pub pub) sec 588 | BS.length sec /= 32 = throwIO CSecp256k1Error 589 | otherwise = 590 A.allocaBytes _SEC_BYTES $ \out -> 591 BS.useAsCString pub $ \(F.castPtr -> pup) -> 592 BS.useAsCString sec $ \(F.castPtr -> sep) -> do 593 suc <- secp256k1_ecdh tex out pup sep F.nullPtr F.nullPtr 594 when (suc /= 1) $ throwIO Secp256k1Error 595 let key = F.castPtr out 596 BS.packCStringLen (key, _SEC_BYTES) 597 598 -- schnorr 599 600 -- | Sign a 32-byte message hash with the provided secret key, using the 601 -- provided 32 bytes of fresh auxiliary entropy. 602 -- 603 -- BIP340 recommends that 32 bytes of fresh auxiliary entropy be 604 -- generated and added at signing time as additional protection 605 -- against side-channel attacks (namely, to thwart so-called "fault 606 -- injection" attacks). This entropy is /supplemental/ to security, 607 -- and the cryptographic security of the signature scheme itself does 608 -- not rely on it, so it is not strictly required; 32 zero bytes can 609 -- be used in its stead. 610 -- 611 -- The resulting 64-byte Schnorr signature is portable, and so is not 612 -- wrapped in a newtype. 613 -- 614 -- >>> import qualified System.Entropy as E -- example entropy source 615 -- >>> enn <- E.getEntropy 32 616 -- >>> aux <- E.getEntropy 32 617 -- >>> wrcontext enn $ \tex -> sign_schnorr tex msg sec aux 618 sign_schnorr 619 :: Context 620 -> BS.ByteString -- ^ 32-byte message hash 621 -> BS.ByteString -- ^ 32-byte secret key 622 -> BS.ByteString -- ^ 32 bytes of fresh entropy 623 -> IO BS.ByteString -- ^ 64-byte signature 624 sign_schnorr c@(Context tex) msg sec aux 625 | BS.length msg /= 32 || BS.length sec /= 32 || BS.length aux /= 32 = 626 throwIO CSecp256k1Error 627 | otherwise = A.allocaBytes _SIG_BYTES $ \out -> 628 BS.useAsCString msg $ \(F.castPtr -> has) -> 629 BS.useAsCString aux $ \(F.castPtr -> enn) -> do 630 KeyPair per <- keypair c sec 631 BS.useAsCString per $ \(F.castPtr -> pur) -> do 632 suc <- secp256k1_schnorrsig_sign32 tex out has pur enn 633 when (suc /= 1) $ throwIO Secp256k1Error 634 let enc = F.castPtr out 635 BS.packCStringLen (enc, _SIG_BYTES) 636 637 -- | Verify a 64-byte Schnorr signature for the provided 32-byte message 638 -- hash with the supplied public key. 639 -- 640 -- >>> wrcontext entropy $ \tex -> verify_schnorr tex pub msg sig 641 verify_schnorr 642 :: Context 643 -> Pub 644 -> BS.ByteString -- ^ 32-byte message hash 645 -> BS.ByteString -- ^ 64-byte signature 646 -> IO Bool 647 verify_schnorr c@(Context tex) pub msg sig 648 | BS.length msg /= 32 || BS.length sig /= 64 = throwIO CSecp256k1Error 649 | otherwise = 650 BS.useAsCString sig $ \(F.castPtr -> sip) -> 651 BS.useAsCStringLen msg $ \(F.castPtr -> has, fromIntegral -> len) -> do 652 XOnlyPub pux <- xonly c pub 653 BS.useAsCString pux $ \(F.castPtr -> pax) -> do 654 suc <- secp256k1_schnorrsig_verify tex sip has len pax 655 pure (suc == 1) 656