packages feed

ppad-fixed 0.1.6 → 0.2.0

raw patch · 11 files changed

+82/−11 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG view
@@ -1,5 +1,17 @@ # Changelog +- 0.2.0 (2026-08-01)+  * Addresses a couple of minor issues detected by an LLM scan:++    * Fixes a bug that could lead shl1_c# to produce a malformed Choice+      that would behave incorrectly for negation or selection.++    * Each Montgomery domain's 'signum' implementation was broken,+      producing R^-1 instead of the proper in-domain 'one'. Now these+      return the appropriate values.++    Also clarifies a few Haddocks, on the suggestions of the same scan.+ - 0.1.6 (2026-08-01)   * The eq_{word,wide,wider}# functions now return a mask, instead of a     bit, inside the resulting Choice value.
lib/Data/Choice.hs view
@@ -305,6 +305,9 @@  -- | Select an unboxed word without branching, given a 'Choice'. --+--   Returns the second word if the 'Choice' is truthy, and the first+--   otherwise.+-- --   >>> let w = C.select_word# 0## 1## (C.true# ()) in GHC.Word.W# w --   1 select_word# :: Word# -> Word# -> Choice -> Word#@@ -312,6 +315,9 @@ {-# INLINE select_word# #-}  -- | Select an unboxed two-limb word without branching, given a 'Choice'.+--+--   Returns the second word if the 'Choice' is truthy, and the first+--   otherwise. select_wide#   :: Limb2   -> Limb2@@ -323,6 +329,9 @@ {-# INLINE select_wide# #-}  -- | Select an unboxed four-limb word without branching, given a 'Choice'.+--+--   Returns the second word if the 'Choice' is truthy, and the first+--   otherwise. select_wider#   :: Limb4   -> Limb4
lib/Data/Word/Limb.hs view
@@ -143,7 +143,7 @@  -- constant-time selection ---------------------------------------------------- --- | Return a if c is truthy, otherwise return b.+-- | Return b if c is truthy, otherwise return a. select#   :: Limb     -- ^ a   -> Limb     -- ^ b@@ -268,6 +268,9 @@  -- | Carrying addition, computing augend + addend + carry, returning --   the sum and new carry.+--+--   The carry-in must be 0 or 1; this is not checked. The carry-out+--   is always 0 or 1. add_c#   :: Limb             -- ^ augend   -> Limb             -- ^ addend@@ -304,6 +307,11 @@  -- | Borrowing subtraction, computing minuend - (subtrahend + borrow), --   returning the difference and new borrow mask.+--+--   The borrow-in is interpreted as a mask, of which only the most+--   significant bit is examined; a full-word mask (as produced by+--   this function) borrows 1. The borrow-out is 0 or a full-word+--   mask. sub_b#   :: Limb              -- ^ minuend   -> Limb              -- ^ subtrahend
lib/Data/Word/Wide.hs view
@@ -159,7 +159,7 @@  -- constant-time selection----------------------------------------------------- --- | Return a if c is truthy, otherwise return b.+-- | Return b if c is truthy, otherwise return a. -- --   >>> import qualified Data.Choice as C --   >>> select 0 1 (C.true# ())
lib/Data/Word/Wider.hs view
@@ -319,7 +319,7 @@   in  L4 w0 w1 w2 w3 {-# INLINE select# #-} --- | Return a if c is truthy, otherwise return b.+-- | Return b if c is truthy, otherwise return a. -- --   >>> import qualified Data.Choice as C --   >>> select 0 1 (C.true# ())@@ -384,7 +384,7 @@       !r2           = L.or# s2 c1       !(# s3, c3 #) = (# L.shl# w3 1#, L.shr# w3 s #)       !r3           = L.or# s3 c2-      !(Limb w)     = L.shl# c3 s+      !(Limb w)     = c3   in  (# (# r0, r1, r2, r3 #), C.from_bit# w #) {-# INLINE shl1_c# #-} 
lib/Numeric/Montgomery/Secp256k1/Curve.hs view
@@ -109,8 +109,9 @@   signum (Montgomery (# l0, l1, l2, l3 #)) =     let !(Limb l) = l0 `L.or#` l1 `L.or#` l2 `L.or#` l3         !n = C.from_word_nonzero# l-        !b = C.to_word# n-    in  Montgomery (# Limb b, Limb 0##, Limb 0##, Limb 0## #)+        !(Montgomery z) = zero+        !(Montgomery o) = one+    in  Montgomery (select# z o n)  instance NFData Montgomery where   rnf (Montgomery a) = case a of (# _, _, _, _ #) -> ()@@ -761,6 +762,9 @@  -- | Multiplicative inverse in the Montgomery domain. --+--   Note that 'zero' has no multiplicative inverse; 'inv' returns+--   'zero' when applied to it.+-- --   >> inv 2 --   57896044618658097711785492504343953926634992332820282019728792003954417335832 --   >> inv 2 * 2@@ -1354,7 +1358,7 @@ select# = WW.select# {-# INLINE select# #-} --- | Return a if c is truthy, otherwise return b.+-- | Return b if c is truthy, otherwise return a. -- --   >>> import qualified Data.Choice as C --   >>> select 0 1 (C.true# ())
lib/Numeric/Montgomery/Secp256k1/Scalar.hs view
@@ -107,8 +107,9 @@   signum (Montgomery (# l0, l1, l2, l3 #)) =     let !(Limb l) = l0 `L.or#` l1 `L.or#` l2 `L.or#` l3         !n = C.from_word_nonzero# l-        !b = C.to_word# n-    in  Montgomery (L4 b 0## 0## 0##)+        !(Montgomery z) = zero+        !(Montgomery o) = one+    in  Montgomery (select# z o n)  instance NFData Montgomery where   rnf (Montgomery a) = case a of (# _, _, _, _ #) -> ()@@ -792,6 +793,9 @@  -- | Multiplicative inverse in the Montgomery domain. --+--   Note that 'zero' has no multiplicative inverse; 'inv' returns+--   'zero' when applied to it.+-- --   >> inv 2 --   57896044618658097711785492504343953926418782139537452191302581570759080747169 --   >> inv 2 * 2@@ -856,7 +860,7 @@ select# = WW.select# {-# INLINE select# #-} --- | Return a if c is truthy, otherwise return b.+-- | Return b if c is truthy, otherwise return a. -- --   >>> import qualified Data.Choice as C --   >>> select 0 1 (C.true# ())
ppad-fixed.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               ppad-fixed-version:            0.1.6+version:            0.2.0 synopsis:           Large fixed-width words and constant-time arithmetic. license:            MIT license-file:       LICENSE
test/Montgomery/Curve.hs view
@@ -118,6 +118,13 @@     0x00000000000000000000000000000000000000000000000000000001000003D1     0x000000000000000000000000000000000000000000000001000007A2000E90A1 +signum_correct :: H.Assertion+signum_correct = do+  H.assertBool "signum 0 = 0" (C.eq_vartime (signum 0) 0)+  H.assertBool "signum 5 = 1" (C.eq_vartime (signum 5) 1)+  H.assertBool "abs 5 * signum 5 = 5"+    (C.eq_vartime (abs 5 * signum 5) 5)+ instance Q.Arbitrary W.Wider where   arbitrary = fmap W.to_vartime Q.arbitrary @@ -177,6 +184,7 @@   , H.testCase "add" add   , H.testCase "sub" sub   , H.testCase "mul" mul+  , H.testCase "signum" signum_correct   , Q.testProperty "a + b mod m ~ ma + mb" $ Q.withMaxSuccess 500 add_matches   , Q.testProperty "a * b mod m ~ ma * mb" $ Q.withMaxSuccess 500 mul_matches   , Q.testProperty "a ^ 2 mod m ~ ma ^ 2"  $ Q.withMaxSuccess 500 sqr_matches
test/Montgomery/Scalar.hs view
@@ -118,6 +118,13 @@     0x000000000000000000000000000000014551231950B75FC4402DA1732FC9BEBF     0x9D671CD581C69BC5E697F5E45BCD07C6741496C20E7CF878896CF21467D7D140 +signum_correct :: H.Assertion+signum_correct = do+  H.assertBool "signum 0 = 0" (S.eq_vartime (signum 0) 0)+  H.assertBool "signum 5 = 1" (S.eq_vartime (signum 5) 1)+  H.assertBool "abs 5 * signum 5 = 5"+    (S.eq_vartime (abs 5 * signum 5) 5)+ instance Q.Arbitrary W.Wider where   arbitrary = fmap W.to_vartime Q.arbitrary @@ -173,6 +180,7 @@   , H.testCase "add" add   , H.testCase "sub" sub   , H.testCase "mul" mul+  , H.testCase "signum" signum_correct   , Q.testProperty "a + b mod m ~ ma + mb" $ Q.withMaxSuccess 500 add_matches   , Q.testProperty "a * b mod m ~ ma * mb" $ Q.withMaxSuccess 500 mul_matches   , Q.testProperty "a ^ 2 mod m ~ ma ^ 2"  $ Q.withMaxSuccess 500 sqr_matches
test/Wider.hs view
@@ -183,6 +183,23 @@       !e = 0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56   H.assertBool mempty (W.eq_vartime o e) +shl1_c :: H.Assertion+shl1_c = case W.shl1_c (2 ^ (255 :: Word)) of+  (# r1, c1 #) -> case W.shl1_c 1 of+    (# r2, c2 #) -> do+      H.assertBool "value, carry out" (W.eq_vartime r1 0)+      H.assertBool "carry decides true" (C.decide c1)+      -- the carry must be a full-word mask, not a bare bit; negating+      -- or selecting on it is otherwise wrong+      H.assertBool "negated carry decides false"+        (not (C.decide (C.not c1)))+      H.assertBool "select on carry"+        (W.eq_vartime (W.select 0 1 c1) 1)+      H.assertBool "value, no carry" (W.eq_vartime r2 2)+      H.assertBool "no carry decides false" (not (C.decide c2))+      H.assertBool "select on no carry"+        (W.eq_vartime (W.select 0 1 c2) 0)+ instance Q.Arbitrary W.Wider where   arbitrary = fmap W.to_vartime Q.arbitrary @@ -216,6 +233,7 @@   , H.testCase "sqr" sqr   , H.testCase "mul" mul   , H.testCase "sub_mod" sub_mod+  , H.testCase "shl1_c" shl1_c   , Q.testProperty "odd w ~ odd (from w)" $ Q.withMaxSuccess 500 odd_correct   , Q.testProperty "lt_vartime a b ~ from a < from b" $       Q.withMaxSuccess 500 lt_vartime_correct