Harness.hs (3575B)
1 {-# LANGUAGE BangPatterns #-} 2 {-# LANGUAGE ForeignFunctionInterface #-} 3 4 -- | ppad-secp256k1 under vestigia: each run draws a fresh secret key 5 -- (and Schnorr auxiliary randomness), marked secret, and runs the 6 -- library's secret-key roots on fixed public messages: public key 7 -- derivation, Schnorr and ECDSA signing (with and without a 8 -- precomputed context), and ECDH against a fixed public peer. 9 -- 10 -- usage: harness [RUNS [SEED]] 11 -- 12 -- SEED (hex) seeds the secrets alone, so runs that differ in SEED 13 -- differ only in their secrets. 14 15 module Main ( 16 main 17 ) where 18 19 import Control.Monad (forM_) 20 import qualified Crypto.Curve.Secp256k1 as S 21 import Data.Bits (shiftR, xor, (.&.)) 22 import qualified Data.ByteString as BS 23 import qualified Data.ByteString.Unsafe as BU 24 import Data.IORef (modifyIORef', newIORef, readIORef, writeIORef) 25 import Data.Word (Word64) 26 import Foreign.C.Types (CSize(..)) 27 import Foreign.Ptr (Ptr, castPtr) 28 import Numeric (readHex) 29 import System.Environment (getArgs) 30 31 foreign import ccall unsafe "vestigia_secret" 32 c_secret :: Ptr () -> CSize -> IO () 33 34 foreign import ccall unsafe "vestigia_public" 35 c_public :: Ptr () -> CSize -> IO () 36 37 mark :: (Ptr () -> CSize -> IO ()) -> BS.ByteString -> IO () 38 mark f bs = 39 BU.unsafeUseAsCStringLen bs (\(p, n) -> f (castPtr p) (fromIntegral n)) 40 41 splitmix :: Word64 -> (Word64, Word64) 42 splitmix s0 = 43 let s = s0 + 0x9e3779b97f4a7c15 44 z1 = (s `xor` (s `shiftR` 30)) * 0xbf58476d1ce4e5b9 45 z2 = (z1 `xor` (z1 `shiftR` 27)) * 0x94d049bb133111eb 46 in (z2 `xor` (z2 `shiftR` 31), s) 47 48 bytes :: Int -> Word64 -> (BS.ByteString, Word64) 49 bytes n s0 = go n s0 [] 50 where 51 go 0 s acc = (BS.pack (reverse acc), s) 52 go k s acc = 53 let (x, s') = splitmix s in go (k - 1) s' (fromIntegral x : acc) 54 55 -- a key below the group order: random bytes with the top bit clear 56 key :: Word64 -> (BS.ByteString, Word64) 57 key s = 58 let (b, s') = bytes 32 s 59 in case BS.uncons b of 60 Just (h, t) -> (BS.cons (h .&. 0x7f) t, s') 61 Nothing -> (b, s') 62 63 -- a result's bytes, made public again: a signature is meant to be 64 -- revealed 65 reveal :: Maybe BS.ByteString -> IO Int 66 reveal r = case r of 67 Just b -> mark c_public b >> pure (BS.length b) 68 Nothing -> pure 0 69 70 -- a result forced, and nothing of it revealed 71 forced :: Maybe a -> IO Int 72 forced r = case r of 73 Just !_ -> pure 1 74 Nothing -> pure 0 75 76 main :: IO () 77 main = do 78 args <- getArgs 79 let runs = case args of 80 (r : _) | [ (n, "") ] <- reads r -> n 81 _ -> 2 :: Int 82 seed = case args of 83 (_ : s : _) | [ (n, "") ] <- readHex s -> n 84 _ -> 1 85 (msg, p1) = bytes 32 0x0123456789abcdef 86 (peerKey, _) = key p1 87 !tex = S.precompute 88 peer <- case S.parse_int256 peerKey >>= S.derive_pub of 89 Just p -> pure p 90 Nothing -> fail "no peer key" 91 secrets <- newIORef seed 92 total <- newIORef (0 :: Int) 93 forM_ [ 1 .. runs ] $ \_ -> do 94 s <- readIORef secrets 95 let (skBytes, s1) = key s 96 (aux, s2) = bytes 32 s1 97 writeIORef secrets s2 98 mark c_secret skBytes 99 mark c_secret aux 100 case S.parse_int256 skBytes of 101 Nothing -> pure () 102 Just sk -> do 103 n1 <- forced (S.derive_pub sk) 104 n2 <- reveal (S.sign_schnorr sk msg aux) 105 n3 <- reveal (S.sign_schnorr' tex sk msg aux) 106 n4 <- forced (S.sign_ecdsa sk msg) 107 n5 <- forced (S.sign_ecdsa' tex sk msg) 108 n6 <- forced (S.ecdh peer sk) 109 modifyIORef' total (+ (n1 + n2 + n3 + n4 + n5 + n6)) 110 n <- readIORef total 111 putStrLn (show runs <> " run(s), " <> show n <> " result(s)")