packages feed

crypton-2.2.5: Crypto/Number/ModArithmetic/Pub.hs

{-# LANGUAGE BangPatterns #-}

-- |
-- Module      : Crypto.Number.ModArithmetic.Pub
-- License     : BSD-style
-- Stability   : experimental
-- Portability : Good
--
-- The C exponentiation for an exponent that is not a secret, and the R^2
-- it keeps per modulus.  Internal: 'expPub' is behind 'expFast', and
-- 'expPubBytes' behind RSA's public-key operation.
module Crypto.Number.ModArithmetic.Pub (
    expPub,
    expPubBytes,
) where

import Crypto.Internal.ByteArray (
    ByteArray,
    ByteArrayAccess,
    Bytes,
    allocRet,
    withByteArray,
 )
import qualified Crypto.Internal.ByteArray as B
import Crypto.Internal.Compat (unsafeDoIO)
import Crypto.Number.Basic
import qualified Crypto.Number.Serialize.Internal as Internal
import Data.IORef (IORef, atomicModifyIORef', newIORef)
import Data.Word (Word32, Word8)
import Foreign.C.Types (CInt (..))
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (Ptr, plusPtr)
import System.IO.Unsafe (unsafePerformIO)

-- | The windowless exponentiation in C, for an exponent that is not a
--   secret.  'Nothing' when the C declines, which it does on every machine
--   where what the caller already has is faster -- so this is an
--   optimisation and never the only path.  The base has to be reduced and
--   the exponent positive, which the caller sees to.
expPub :: Integer -> Integer -> Integer -> Maybe Integer
expPub b e m = unsafeDoIO $ do
    known <- r2For m
    case known of
        Nothing -> return Nothing
        Just (r2, modu) ->
            allocaBytes (sum widths) $ \start -> case scanl plusPtr start widths of
                (out : base : expo : _) -> do
                    _ <- Internal.i2ospOf b base mLen
                    _ <- Internal.i2ospOf e expo eLen
                    r <- withByteArray modu $ \pm -> withByteArray r2 $ \pr2 ->
                        c_powm_pub_r2
                            out
                            base
                            (fromIntegral mLen)
                            expo
                            (fromIntegral eLen)
                            pm
                            (fromIntegral mLen)
                            pr2
                    if r == 0
                        then do
                            !v <- Internal.os2ip out mLen
                            return (Just v)
                        else return Nothing
                _ -> return Nothing
  where
    !mLen = numBytes m
    !eLen = numBytes e
    widths = [mLen, mLen, eLen]

-- | The same with the base and the answer as big-endian bytes, as long as
--   the modulus: what RSA verification has in hand, so neither goes through
--   an 'Integer'.  'Nothing' also when the base is not below the modulus.
expPubBytes
    :: (ByteArrayAccess bi, ByteArray bo) => Integer -> Integer -> bi -> Maybe bo
expPubBytes e m b
    | e <= 0 || m <= 1 || even m || B.length b /= mLen = Nothing
    | otherwise = unsafeDoIO $ do
        known <- r2For m
        case known of
            Nothing -> return Nothing
            Just (r2, modu)
                | not (below b modu) -> return Nothing
                | otherwise -> allocaBytes eLen $ \expo -> do
                    _ <- Internal.i2ospOf e expo eLen
                    (r, out) <- allocRet mLen $ \pout ->
                        withByteArray b $ \pb -> withByteArray modu $ \pm ->
                            withByteArray r2 $ \pr2 ->
                                c_powm_pub_r2
                                    pout
                                    pb
                                    (fromIntegral mLen)
                                    expo
                                    (fromIntegral eLen)
                                    pm
                                    (fromIntegral mLen)
                                    pr2
                    return (if r == 0 then Just out else Nothing)
  where
    !mLen = numBytes m
    !eLen = numBytes e

-- big-endian numbers of one length: the first byte that differs decides
below :: (ByteArrayAccess a, ByteArrayAccess b) => a -> b -> Bool
below x y = go 0
  where
    n = B.length x
    go i
        | i >= n = False
        | otherwise = case compare (B.index x i) (B.index y i) of
            EQ -> go (i + 1)
            o -> o == LT

-- | R^2 mod m for the public path, and m in bytes, kept for the last few
-- moduli, since a program verifies under the same keys again and again.
-- 'Nothing' is kept too: it means the C declines this modulus, which is
-- then not asked again.
r2For :: Integer -> IO (Maybe (Bytes, Bytes))
r2For m = do
    found <- atomicModifyIORef' r2Table $ \ks -> case break ((== m) . fst) ks of
        (before, hit : after) -> (hit : before ++ after, Just (snd hit))
        _ -> (ks, Nothing)
    case found of
        Just r2 -> return r2
        Nothing -> do
            let mLen = numBytes m
            (r2, modu) <- allocRet mLen $ \modu -> do
                _ <- Internal.i2ospOf m modu mLen
                (r, out) <- allocRet mLen $ \pout -> c_powm_r2 pout modu (fromIntegral mLen)
                return (if r == 0 then Just out else Nothing)
            let kept = fmap (\x -> (x, modu)) r2
            atomicModifyIORef' r2Table $ \ks -> (take r2TableSize ((m, kept) : ks), ())
            return kept

r2TableSize :: Int
r2TableSize = 8

r2Table :: IORef [(Integer, Maybe (Bytes, Bytes))]
r2Table = unsafePerformIO (newIORef [])
{-# NOINLINE r2Table #-}

-- safe, as the secure one is: the call is tens of microseconds at RSA
-- sizes, and the hundred nanoseconds this costs over an unsafe call is
-- nothing beside holding a capability for that long in a server
foreign import ccall safe "crypton_powm_pub_r2"
    c_powm_pub_r2
        :: Ptr Word8
        -> Ptr Word8
        -> Word32
        -> Ptr Word8
        -> Word32
        -> Ptr Word8
        -> Word32
        -> Ptr Word8
        -> IO CInt

foreign import ccall unsafe "crypton_powm_r2"
    c_powm_r2 :: Ptr Word8 -> Ptr Word8 -> Word32 -> IO CInt