summaryrefslogtreecommitdiff
path: root/libraries/integer-gmp/src/GHC/Integer/Type.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/integer-gmp/src/GHC/Integer/Type.hs')
-rw-r--r--libraries/integer-gmp/src/GHC/Integer/Type.hs159
1 files changed, 143 insertions, 16 deletions
diff --git a/libraries/integer-gmp/src/GHC/Integer/Type.hs b/libraries/integer-gmp/src/GHC/Integer/Type.hs
index d5f92b32db..3434df29c4 100644
--- a/libraries/integer-gmp/src/GHC/Integer/Type.hs
+++ b/libraries/integer-gmp/src/GHC/Integer/Type.hs
@@ -25,6 +25,7 @@
module GHC.Integer.Type where
#include "MachDeps.h"
+#include "HsIntegerGmp.h"
-- Sanity check as CPP defines are implicitly 0-valued when undefined
#if !(defined(SIZEOF_LONG) && defined(SIZEOF_HSWORD) \
@@ -149,6 +150,11 @@ data Integer = S# !Int#
| Jn# {-# UNPACK #-} !BigNat
-- ^ iff value in @]-inf, minBound::'Int'[@ range
+-- NOTE: the above representation is baked into the GHCi debugger in
+-- compiler/ghci/RtClosureInspect.hs. If you change it here, fixes
+-- will be required over there too. Tests for this are in
+-- testsuite/tests/ghci.debugger.
+
-- TODO: experiment with different constructor-ordering
instance Eq Integer where
@@ -586,15 +592,15 @@ shiftRInteger (Jn# bn) n#
-- Even though the shift-amount is expressed as `Int#`, the result is
-- undefined for negative shift-amounts.
shiftLInteger :: Integer -> Int# -> Integer
-shiftLInteger x 0# = x
+shiftLInteger x 0# = x
shiftLInteger (S# 0#) _ = S# 0#
shiftLInteger (S# 1#) n# = bitInteger n#
shiftLInteger (S# i#) n#
- | isTrue# (i# >=# 0#) = bigNatToInteger (shiftLBigNat
- (wordToBigNat (int2Word# i#)) n#)
- | True = bigNatToNegInteger (shiftLBigNat
- (wordToBigNat (int2Word#
- (negateInt# i#))) n#)
+ | isTrue# (i# >=# 0#) = bigNatToInteger (shiftLBigNat
+ (wordToBigNat (int2Word# i#)) n#)
+ | True = bigNatToNegInteger (shiftLBigNat
+ (wordToBigNat (int2Word#
+ (negateInt# i#))) n#)
shiftLInteger (Jp# bn) n# = Jp# (shiftLBigNat bn n#)
shiftLInteger (Jn# bn) n# = Jn# (shiftLBigNat bn n#)
{-# CONSTANT_FOLDED shiftLInteger #-}
@@ -1059,7 +1065,7 @@ bitBigNat i#
mbn@(MBN# mba#) <- newBigNat# (li# +# 1#)
-- FIXME: do we really need to zero-init MBAs returned by 'newByteArray#'?
-- clear all limbs (except for the most-significant limb)
- _ <- svoid (setByteArray# mba# 0# (li# `uncheckedIShiftL#` GMP_LIMB_SHIFT#) 0#)
+ _ <- svoid (clearWordArray# mba# 0# li#)
-- set single bit in most-significant limb
_ <- svoid (writeBigNat# mbn li# (uncheckedShiftL# 1## bi#))
unsafeFreezeBigNat# mbn
@@ -1090,6 +1096,67 @@ testBitNegBigNat bn i#
allZ j | isTrue# (indexBigNat# bn (j -# 1#) `eqWord#` 0##) = allZ (j -# 1#)
| True = False
+
+clearBitBigNat :: BigNat -> Int# -> BigNat
+clearBitBigNat bn i#
+ | not (inline testBitBigNat bn i#) = bn
+ | isTrue# (nx# ==# 1#) = wordToBigNat (bigNatToWord bn `xor#` bitWord# bi#)
+ | isTrue# (li# +# 1# ==# nx#) = -- special case, operating on most-sig limb
+ case indexBigNat# bn li# `xor#` bitWord# bi# of
+ 0## -> do -- most-sig limb became zero -> result has less limbs
+ case fmssl bn (li# -# 1#) of
+ 0# -> zeroBigNat
+ n# -> runS $ do
+ mbn <- newBigNat# n#
+ _ <- copyWordArray bn 0# mbn 0# n#
+ unsafeFreezeBigNat# mbn
+ newlimb# -> runS $ do -- no shrinking
+ mbn <- newBigNat# nx#
+ _ <- copyWordArray bn 0# mbn 0# li#
+ _ <- svoid (writeBigNat# mbn li# newlimb#)
+ unsafeFreezeBigNat# mbn
+
+ | True = runS $ do
+ mbn <- newBigNat# nx#
+ _ <- copyWordArray bn 0# mbn 0# nx#
+ let newlimb# = indexBigNat# bn li# `xor#` bitWord# bi#
+ _ <- svoid (writeBigNat# mbn li# newlimb#)
+ unsafeFreezeBigNat# mbn
+
+ where
+ !(# li#, bi# #) = quotRemInt# i# GMP_LIMB_BITS#
+ nx# = sizeofBigNat# bn
+
+
+
+setBitBigNat :: BigNat -> Int# -> BigNat
+setBitBigNat bn i#
+ | inline testBitBigNat bn i# = bn
+ | isTrue# (d# ># 0#) = runS $ do -- result BigNat will have more limbs
+ mbn@(MBN# mba#) <- newBigNat# (li# +# 1#)
+ _ <- copyWordArray bn 0# mbn 0# nx#
+ _ <- svoid (clearWordArray# mba# nx# (d# -# 1#))
+ _ <- svoid (writeBigNat# mbn li# (bitWord# bi#))
+ unsafeFreezeBigNat# mbn
+
+ | True = runS $ do
+ mbn <- newBigNat# nx#
+ _ <- copyWordArray bn 0# mbn 0# nx#
+ _ <- svoid (writeBigNat# mbn li# (indexBigNat# bn li#
+ `or#` bitWord# bi#))
+ unsafeFreezeBigNat# mbn
+
+ where
+ !(# li#, bi# #) = quotRemInt# i# GMP_LIMB_BITS#
+ nx# = sizeofBigNat# bn
+ d# = li# +# 1# -# nx#
+
+
+complementBitBigNat :: BigNat -> Int# -> BigNat
+complementBitBigNat bn i#
+ | testBitBigNat bn i# = clearBitBigNat bn i#
+ | True = setBitBigNat bn i#
+
popCountBigNat :: BigNat -> Int#
popCountBigNat bn@(BN# ba#) = word2Int# (c_mpn_popcount ba# (sizeofBigNat# bn))
@@ -1327,7 +1394,9 @@ gcdExtSBigNat x y = case runS go of (g,s) -> (# g, s #)
where
go = do
g@(MBN# g#) <- newBigNat# gn0#
- s@(MBN# s#) <- newBigNat# (absI# xn#)
+ -- According to https://gmplib.org/manual/Number-Theoretic-Functions.html#index-mpz_005fgcdext
+ -- abs(s) < abs(y) / (2 g)
+ s@(MBN# s#) <- newBigNat# (absI# yn#)
I# ssn_# <- liftIO (integer_gmp_gcdext# s# g# x# xn# y# yn#)
let ssn# = narrowGmpSize# ssn_#
sn# = absI# ssn#
@@ -1376,6 +1445,32 @@ powModInteger b e m = case m of
b' = integerToSBigNat b
e' = integerToSBigNat e
+-- | \"@'powModSecInteger' /b/ /e/ /m/@\" computes base @/b/@ raised to
+-- exponent @/e/@ modulo @/m/@. It is required that @/e/ >= 0@ and
+-- @/m/@ is odd.
+--
+-- This is a \"secure\" variant of 'powModInteger' using the
+-- @mpz_powm_sec()@ function which is designed to be resilient to side
+-- channel attacks and is therefore intended for cryptographic
+-- applications.
+--
+-- This primitive is only available when the underlying GMP library
+-- supports it (GMP >= 5). Otherwise, it internally falls back to
+-- @'powModInteger'@, and a warning will be emitted when used.
+--
+-- @since 1.0.2.0
+{-# NOINLINE powModSecInteger #-}
+powModSecInteger :: Integer -> Integer -> Integer -> Integer
+powModSecInteger b e m = bigNatToInteger (powModSecSBigNat b' e' m')
+ where
+ b' = integerToSBigNat b
+ e' = integerToSBigNat e
+ m' = absSBigNat (integerToSBigNat m)
+
+#if HAVE_SECURE_POWM == 0
+{-# WARNING powModSecInteger "The underlying GMP library does not support a secure version of powModInteger which is side-channel resistant - you need at least GMP version 5 to support this" #-}
+#endif
+
-- | Version of 'powModInteger' operating on 'BigNat's
--
-- @since 1.0.0.0
@@ -1428,6 +1523,27 @@ foreign import ccall unsafe "integer_gmp_powm1"
integer_gmp_powm1# :: ByteArray# -> GmpSize# -> ByteArray# -> GmpSize#
-> GmpLimb# -> GmpLimb#
+-- internal non-exported helper
+powModSecSBigNat :: SBigNat -> SBigNat -> BigNat -> BigNat
+powModSecSBigNat b e m@(BN# m#) = runS $ do
+ r@(MBN# r#) <- newBigNat# mn#
+ I# rn_# <- liftIO (integer_gmp_powm_sec# r# b# bn# e# en# m# mn#)
+ let rn# = narrowGmpSize# rn_#
+ case isTrue# (rn# ==# mn#) of
+ False -> unsafeShrinkFreezeBigNat# r rn#
+ True -> unsafeFreezeBigNat# r
+ where
+ !(BN# b#) = absSBigNat b
+ !(BN# e#) = absSBigNat e
+ bn# = ssizeofSBigNat# b
+ en# = ssizeofSBigNat# e
+ mn# = sizeofBigNat# m
+
+foreign import ccall unsafe "integer_gmp_powm_sec"
+ integer_gmp_powm_sec# :: MutableByteArray# RealWorld
+ -> ByteArray# -> GmpSize# -> ByteArray# -> GmpSize#
+ -> ByteArray# -> GmpSize# -> IO GmpSize
+
-- | \"@'recipModInteger' /x/ /m/@\" computes the inverse of @/x/@ modulo @/m/@. If
-- the inverse exists, the return value @/y/@ will satisfy @0 < /y/ <
@@ -1746,6 +1862,15 @@ copyWordArray# src src_ofs dst dst_ofs len
dst (dst_ofs `uncheckedIShiftL#` GMP_LIMB_SHIFT#)
(len `uncheckedIShiftL#` GMP_LIMB_SHIFT#)
+copyWordArray :: BigNat -> Int# -> MutBigNat s -> Int# -> Int# -> S s ()
+copyWordArray (BN# ba#) ofs_ba# (MBN# mba#) ofs_mba# len#
+ = svoid (copyWordArray# ba# ofs_ba# mba# ofs_mba# len#)
+
+clearWordArray# :: MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
+clearWordArray# mba ofs len
+ = setByteArray# mba (ofs `uncheckedIShiftL#` GMP_LIMB_SHIFT#)
+ (len `uncheckedIShiftL#` GMP_LIMB_SHIFT#) 0#
+
-- | Version of 'normSizeofMutBigNat'#' which scans all allocated 'MutBigNat#'
normSizeofMutBigNat# :: MutBigNat s -> State# s -> (# State# s, Int# #)
normSizeofMutBigNat# mbn@(MBN# mba) s = normSizeofMutBigNat'# mbn sz# s'
@@ -1789,13 +1914,7 @@ byteArrayToBigNat# ba# n0#
where
!(# baszq#, baszr# #) = quotRemInt# (sizeofByteArray# ba#) GMP_LIMB_BYTES#
- n# = fmssl (n0# -# 1#)
-
- -- find most significant set limb, return normalized size
- fmssl i#
- | isTrue# (i# <# 0#) = 0#
- | isTrue# (neWord# (indexWordArray# ba# i#) 0##) = i# +# 1#
- | True = fmssl (i# -# 1#)
+ n# = fmssl (BN# ba#) (n0# -# 1#)
-- | Read 'Integer' (without sign) from memory location at @/addr/@ in
-- base-256 representation.
@@ -1996,7 +2115,7 @@ intToSBigNat# 0# = PosBN zeroBigNat
intToSBigNat# 1# = PosBN oneBigNat
intToSBigNat# (-1#) = NegBN oneBigNat
intToSBigNat# i# | isTrue# (i# ># 0#) = PosBN (wordToBigNat (int2Word# i#))
- | True = PosBN (wordToBigNat (int2Word# (negateInt# i#)))
+ | True = NegBN (wordToBigNat (int2Word# (negateInt# i#)))
-- | Convert 'Integer' into 'SBigNat'
integerToSBigNat :: Integer -> SBigNat
@@ -2048,3 +2167,11 @@ cmpI# x# y# = (x# ># y#) -# (x# <# y#)
minI# :: Int# -> Int# -> Int#
minI# x# y# | isTrue# (x# <=# y#) = x#
| True = y#
+
+-- find most-sig set limb, starting at given index
+fmssl :: BigNat -> Int# -> Int#
+fmssl !bn i0# = go i0#
+ where
+ go i# | isTrue# (i# <# 0#) = 0#
+ | isTrue# (neWord# (indexBigNat# bn i#) 0##) = i# +# 1#
+ | True = go (i# -# 1#)