packages feed

crypton-2.2.4: Crypto/PubKey/RSA/Blinded/Internal.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}

-- |
-- Module      : Crypto.PubKey.RSA.Blinded.Internal
-- License     : BSD-style
-- Stability   : experimental
-- Portability : Good
--
-- A private key with a blinding pair that is reused by squaring, as OpenSSL
-- does, rather than generated afresh for every operation.  The key is a value
-- threaded through the operations, as a hash 'Crypto.Hash.Context' is.
module Crypto.PubKey.RSA.Blinded.Internal (
    BlindedKey (..),
    blindedKey,
    dpBlinded,
    Draw,
    drawFor,
    withBlindingFor,
    dpWithPair,
) where

import Data.IORef
import System.IO.Unsafe (unsafePerformIO)

import Crypto.Internal.ByteArray (ByteArray, ScrubbedBytes)
import Crypto.Number.Generate (generateMax)
import Crypto.Number.ModArithmetic (expFast, inverseCoprimes)
import Crypto.Number.Serialize (i2ospOf_, os2ip)
import Crypto.PubKey.RSA.Prim (dp)
import Crypto.PubKey.RSA.Types
import Crypto.Random.Types

-- | A private key together with the blinding its next operation uses.
--
-- Each operation multiplies the input by @r^e@ and the output by @r^-1@, and
-- returns the key with both squared for the next one, so a pair costs two
-- multiplications after the first.  A new @r@ is drawn every 32 operations.
-- Use the key an operation returns: handing the same one to two operations
-- gives both the same pair.
data BlindedKey = BlindedKey
    { blindedPrivateKey :: PrivateKey
    -- ^ the key it was made from
    , blindedPair :: Pair
    }

-- | @r^e@ and @r^-1@ modulo n, and how many more uses they have after this.
data Pair = Pair !Int !Integer !Integer | Spent

-- | Uses of one @r@ before another is drawn, OpenSSL's number.
refresh :: Int
refresh = 32

-- | A t'BlindedKey' for a private key.  The first pair is drawn when it is
-- first used.
blindedKey :: PrivateKey -> BlindedKey
blindedKey pk = BlindedKey pk Spent

-- | The random numbers a new pair is made from: drawing them is cheap, and
-- making the pair, an inverse and an exponentiation, is done only if needed.
data Draw = Draw !Integer !Integer

-- | Draw for a key, as 'Crypto.PubKey.RSA.generateBlinder' does.
drawFor :: forall m. MonadRandom m => PrivateKey -> m Draw
drawFor pk = do
    r <- generateMax (private_n pk)
    u <- os2ip <$> (getRandomBytes 16 :: m ScrubbedBytes)
    return (Draw r u)

-- | @r^e@ and @r^-1@.  The inverse is taken of @r*u@ and multiplied by @u@,
-- so the extended Euclidean algorithm never sees @r@, as in
-- 'Crypto.PubKey.RSA.generateBlinder'.
makePair :: PrivateKey -> Draw -> (Integer, Integer)
makePair pk (Draw r u) =
    (expFast r (private_e pk) n, (inverseCoprimes ((r * u) `mod` n) n * u) `mod` n)
  where
    n = private_n pk

-- | The pair for this operation, and the key holding the next one.  The
-- draw is used only when a new pair is due.
takePair :: Draw -> BlindedKey -> ((Integer, Integer), BlindedKey)
takePair d (BlindedKey pk pair) = case pair of
    Pair k a ai -> ((a, ai), BlindedKey pk (next k a ai))
    Spent -> case makePair pk d of
        (a, ai) -> ((a, ai), BlindedKey pk (next refresh a ai))
  where
    n = private_n pk
    next k a ai
        | k > 1 = Pair (k - 1) ((a * a) `mod` n) ((ai * ai) `mod` n)
        | otherwise = Spent

-- | The decryption primitive, blinded with a given pair.
dpWithPair :: ByteArray ba => (Integer, Integer) -> PrivateKey -> ba -> ba
dpWithPair (a, ai) pk c = i2ospOf_ k ((ai * os2ip m) `mod` n)
  where
    n = private_n pk
    k = private_size pk
    m = dp Nothing pk (i2ospOf_ k ((a * os2ip c) `mod` n) `asTypeOf` c)

-- | The decryption primitive, blinded with the key's pair, and the key for
-- the next operation.  Randomness is drawn only when a new pair is due.
dpBlinded
    :: (MonadRandom m, ByteArray ba) => BlindedKey -> ba -> m (ba, BlindedKey)
dpBlinded bk c = do
    d <- case blindedPair bk of
        Pair {} -> return (Draw 0 0)
        Spent -> drawFor (blindedPrivateKey bk)
    let (pair, bk') = takePair d bk
    return (dpWithPair pair (blindedPrivateKey bk) c, bk')

-- | Run an operation with the pair kept for a key, for the functions that
-- take a bare t'PrivateKey'.  One t'BlindedKey' is kept per public key, for
-- the last few keys used; taking its pair and leaving the next is atomic, so
-- no two operations get the same pair.  The caller draws every time, since
-- whether a new pair is due is known only in here.
withBlindingFor :: PrivateKey -> Draw -> ((Integer, Integer) -> a) -> a
withBlindingFor pk d f = unsafePerformIO $ do
    pair <- atomicModifyIORef' table step
    return (f pair)
  where
    pub = private_pub pk
    step ks = case break ((== pub) . private_pub . blindedPrivateKey) ks of
        (before, bk : after) -> found bk (before ++ after)
        _ -> found (blindedKey pk) ks
    found bk rest = case takePair d bk of
        (pair@(!_, !_), bk') -> (take tableSize (bk' : rest), pair)
{-# NOINLINE withBlindingFor #-}

-- | How many keys 'withBlindingFor' remembers.
tableSize :: Int
tableSize = 4

table :: IORef [BlindedKey]
table = unsafePerformIO (newIORef [])
{-# NOINLINE table #-}