diff --git a/.gitignore b/.gitignore index 818d718..e816dd7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.tix *.mix gen/Gen +gen/Crypto dist diff --git a/Crypto/PubKey/DH.hs b/Crypto/PubKey/DH.hs new file mode 100644 index 0000000..9008641 --- /dev/null +++ b/Crypto/PubKey/DH.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +-- | +-- Module : Crypto.PubKey.DH +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +{-# LANGUAGE DeriveDataTypeable #-} +module Crypto.PubKey.DH + ( Params(..) + , PublicNumber(..) + , PrivateNumber(..) + , SharedKey(..) + , generateParams + , generatePrivate + , calculatePublic + , generatePublic + , getShared + ) where + +import Control.Applicative +import Crypto.Number.ModArithmetic (expSafe) +import Crypto.Number.Prime (generateSafePrime) +import Crypto.Number.Generate (generateMax) +import Crypto.Random.Types +import Data.Data + +-- | Represent Diffie Hellman parameters namely P (prime), and G (generator). +data Params = Params + { params_p :: Integer + , params_g :: Integer + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent Diffie Hellman public number Y. +newtype PublicNumber = PublicNumber Integer + deriving (Show,Read,Eq,Enum,Real,Num,Ord) + +-- | Represent Diffie Hellman private number X. +newtype PrivateNumber = PrivateNumber Integer + deriving (Show,Read,Eq,Enum,Real,Num,Ord) + +-- | Represent Diffie Hellman shared secret. +newtype SharedKey = SharedKey Integer + deriving (Show,Read,Eq,Enum,Real,Num,Ord) + +-- | generate params from a specific generator (2 or 5 are common values) +-- we generate a safe prime (a prime number of the form 2p+1 where p is also prime) +generateParams :: MonadRandom m => Int -> Integer -> m Params +generateParams bits generator = + (\p -> Params p generator) <$> generateSafePrime bits + +-- | generate a private number with no specific property +-- this number is usually called X in DH text. +generatePrivate :: MonadRandom m => Params -> m PrivateNumber +generatePrivate (Params p _) = PrivateNumber <$> generateMax p + +-- | calculate the public number from the parameters and the private key +-- this number is usually called Y in DH text. +calculatePublic :: Params -> PrivateNumber -> PublicNumber +calculatePublic (Params p g) (PrivateNumber x) = PublicNumber $ expSafe g x p + +-- | calculate the public number from the parameters and the private key +-- this number is usually called Y in DH text. +-- +-- DEPRECATED use calculatePublic +generatePublic :: Params -> PrivateNumber -> PublicNumber +generatePublic = calculatePublic +-- commented until 0.3 {-# DEPRECATED generatePublic "use calculatePublic" #-} + +-- | generate a shared key using our private number and the other party public number +getShared :: Params -> PrivateNumber -> PublicNumber -> SharedKey +getShared (Params p _) (PrivateNumber x) (PublicNumber y) = SharedKey $ expSafe y x p diff --git a/Crypto/PubKey/DSA.hs b/Crypto/PubKey/DSA.hs new file mode 100644 index 0000000..afa15ca --- /dev/null +++ b/Crypto/PubKey/DSA.hs @@ -0,0 +1,130 @@ +-- | +-- Module : Crypto.PubKey.DSA +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +-- An implementation of the Digital Signature Algorithm (DSA) +{-# LANGUAGE DeriveDataTypeable #-} +module Crypto.PubKey.DSA + ( Params(..) + , Signature(..) + , PublicKey(..) + , PrivateKey(..) + -- * generation + , generatePrivate + , calculatePublic + -- * signature primitive + , sign + , signWith + -- * verification primitive + , verify + ) where + +import Crypto.Random.Types +import Data.Data +import Data.Maybe +import Data.ByteString (ByteString) +import Crypto.Number.ModArithmetic (expFast, expSafe, inverse) +import Crypto.Number.Serialize +import Crypto.Number.Generate +import Crypto.PubKey.HashDescr + +-- | DSA Public Number, usually embedded in DSA Public Key +type PublicNumber = Integer + +-- | DSA Private Number, usually embedded in DSA Private Key +type PrivateNumber = Integer + +-- | Represent DSA parameters namely P, G, and Q. +data Params = Params + { params_p :: Integer -- ^ DSA p + , params_g :: Integer -- ^ DSA g + , params_q :: Integer -- ^ DSA q + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent a DSA signature namely R and S. +data Signature = Signature + { sign_r :: Integer -- ^ DSA r + , sign_s :: Integer -- ^ DSA s + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent a DSA public key. +data PublicKey = PublicKey + { public_params :: Params -- ^ DSA parameters + , public_y :: PublicNumber -- ^ DSA public Y + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent a DSA private key. +-- +-- Only x need to be secret. +-- the DSA parameters are publicly shared with the other side. +data PrivateKey = PrivateKey + { private_params :: Params -- ^ DSA parameters + , private_x :: PrivateNumber -- ^ DSA private X + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent a DSA key pair +data KeyPair = KeyPair Params PublicNumber PrivateNumber + deriving (Show,Read,Eq,Data,Typeable) + +-- | Public key of a DSA Key pair +toPublicKey :: KeyPair -> PublicKey +toPublicKey (KeyPair params pub _) = PublicKey params pub + +-- | Private key of a DSA Key pair +toPrivateKey :: KeyPair -> PrivateKey +toPrivateKey (KeyPair params _ priv) = PrivateKey params priv + +-- | generate a private number with no specific property +-- this number is usually called X in DSA text. +generatePrivate :: MonadRandom m => Params -> m PrivateNumber +generatePrivate (Params _ _ q) = generateMax q + +-- | Calculate the public number from the parameters and the private key +calculatePublic :: Params -> PrivateNumber -> PublicNumber +calculatePublic (Params p g _) x = expSafe g x p + +-- | sign message using the private key and an explicit k number. +signWith :: Integer -- ^ k random number + -> PrivateKey -- ^ private key + -> HashFunction -- ^ hash function + -> ByteString -- ^ message to sign + -> Maybe Signature +signWith k pk hash msg + | r == 0 || s == 0 = Nothing + | otherwise = Just $ Signature r s + where -- parameters + (Params p g q) = private_params pk + x = private_x pk + -- compute r,s + kInv = fromJust $ inverse k q + hm = os2ip $ hash msg + r = expSafe g k p `mod` q + s = (kInv * (hm + x * r)) `mod` q + +-- | sign message using the private key. +sign :: MonadRandom m => PrivateKey -> HashFunction -> ByteString -> m Signature +sign pk hash msg = do + k <- generateMax q + case signWith k pk hash msg of + Nothing -> sign pk hash msg + Just sig -> return sig + where + (Params _ _ q) = private_params pk + +-- | verify a bytestring using the public key. +verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool +verify hash pk (Signature r s) m + -- Reject the signature if either 0 < r < q or 0 < s < q is not satisfied. + | r <= 0 || r >= q || s <= 0 || s >= q = False + | otherwise = v == r + where (Params p g q) = public_params pk + y = public_y pk + hm = os2ip $ hash m + + w = fromJust $ inverse s q + u1 = (hm*w) `mod` q + u2 = (r*w) `mod` q + v = ((expFast g u1 p) * (expFast y u2 p)) `mod` p `mod` q diff --git a/Crypto/PubKey/ECC/DH.hs b/Crypto/PubKey/ECC/DH.hs new file mode 100644 index 0000000..c2b7fd4 --- /dev/null +++ b/Crypto/PubKey/ECC/DH.hs @@ -0,0 +1,36 @@ +module Crypto.PubKey.ECC.DH ( + Curve + , PublicPoint + , PrivateNumber + , SharedKey(..) + , generatePrivate + , calculatePublic + , getShared + ) where + +import Crypto.Number.Generate (generateMax) +import Crypto.PubKey.ECC.Prim (pointMul) +import Crypto.Random.Types +import Crypto.PubKey.DH (SharedKey(..)) +import Crypto.PubKey.ECC.Types (PublicPoint, PrivateNumber, Curve, Point(..)) +import Crypto.PubKey.ECC.Types (ecc_n, ecc_g, common_curve) + +-- | Generating a private number d. +generatePrivate :: MonadRandom m => Curve -> m PrivateNumber +generatePrivate curve = generateMax n + where + n = ecc_n $ common_curve curve + +-- | Generating a public point Q. +calculatePublic :: Curve -> PrivateNumber -> PublicPoint +calculatePublic curve d = q + where + g = ecc_g $ common_curve curve + q = pointMul curve d g + +-- | Generating a shared key using our private number and +-- the other party public point. +getShared :: Curve -> PrivateNumber -> PublicPoint -> SharedKey +getShared curve db qa = SharedKey x + where + Point x _ = pointMul curve db qa diff --git a/Crypto/PubKey/ECC/ECDSA.hs b/Crypto/PubKey/ECC/ECDSA.hs new file mode 100644 index 0000000..ccee525 --- /dev/null +++ b/Crypto/PubKey/ECC/ECDSA.hs @@ -0,0 +1,119 @@ +-- | /WARNING:/ Signature operations may leak the private key. Signature verification +-- should be safe. +{-# LANGUAGE DeriveDataTypeable #-} +module Crypto.PubKey.ECC.ECDSA + ( Signature(..) + , PublicPoint + , PublicKey(..) + , PrivateNumber + , PrivateKey(..) + , KeyPair(..) + , toPublicKey + , toPrivateKey + , signWith + , sign + , verify + ) where + +import Control.Monad +import Crypto.Random.Types +import Data.Bits (shiftR) +import Data.ByteString (ByteString) +import Data.Data +import Crypto.Number.ModArithmetic (inverse) +import Crypto.Number.Serialize +import Crypto.Number.Generate +import Crypto.PubKey.ECC.Types +import Crypto.PubKey.HashDescr +import Crypto.PubKey.ECC.Prim + +-- | Represent a ECDSA signature namely R and S. +data Signature = Signature + { sign_r :: Integer -- ^ ECDSA r + , sign_s :: Integer -- ^ ECDSA s + } deriving (Show,Read,Eq,Data,Typeable) + +-- | ECDSA Private Key. +data PrivateKey = PrivateKey + { private_curve :: Curve + , private_d :: PrivateNumber + } deriving (Show,Read,Eq,Data,Typeable) + +-- | ECDSA Public Key. +data PublicKey = PublicKey + { public_curve :: Curve + , public_q :: PublicPoint + } deriving (Show,Read,Eq,Data,Typeable) + +-- | ECDSA Key Pair. +data KeyPair = KeyPair Curve PublicPoint PrivateNumber + deriving (Show,Read,Eq,Data,Typeable) + +-- | Public key of a ECDSA Key pair. +toPublicKey :: KeyPair -> PublicKey +toPublicKey (KeyPair curve pub _) = PublicKey curve pub + +-- | Private key of a ECDSA Key pair. +toPrivateKey :: KeyPair -> PrivateKey +toPrivateKey (KeyPair curve _ priv) = PrivateKey curve priv + +-- | Sign message using the private key and an explicit k number. +-- +-- /WARNING:/ Vulnerable to timing attacks. +signWith :: Integer -- ^ k random number + -> PrivateKey -- ^ private key + -> HashFunction -- ^ hash function + -> ByteString -- ^ message to sign + -> Maybe Signature +signWith k (PrivateKey curve d) hash msg = do + let z = tHash hash msg n + CurveCommon _ _ g n _ = common_curve curve + let point = pointMul curve k g + r <- case point of + PointO -> Nothing + Point x _ -> return $ x `mod` n + kInv <- inverse k n + let s = kInv * (z + r * d) `mod` n + when (r == 0 || s == 0) Nothing + return $ Signature r s + +-- | Sign message using the private key. +-- +-- /WARNING:/ Vulnerable to timing attacks. +sign :: MonadRandom m => PrivateKey -> HashFunction -> ByteString -> m Signature +sign pk hash msg = do + k <- generateBetween 1 (n - 1) + case signWith k pk hash msg of + Nothing -> sign pk hash msg + Just sig -> return sig + where n = ecc_n . common_curve $ private_curve pk + +-- | Verify a bytestring using the public key. +verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool +verify _ (PublicKey _ PointO) _ _ = False +verify hash pk@(PublicKey curve q) (Signature r s) msg + | r < 1 || r >= n || s < 1 || s >= n = False + | otherwise = maybe False (r ==) $ do + w <- inverse s n + let z = tHash hash msg n + u1 = z * w `mod` n + u2 = r * w `mod` n + -- TODO: Use Shamir's trick + g' = pointMul curve u1 g + q' = pointMul curve u2 q + x = pointAdd curve g' q' + case x of + PointO -> Nothing + Point x1 _ -> return $ x1 `mod` n + where n = ecc_n cc + g = ecc_g cc + cc = common_curve $ public_curve pk + +-- | Truncate and hash. +tHash :: HashFunction -> ByteString -> Integer -> Integer +tHash hash m n + | d > 0 = shiftR e d + | otherwise = e + where e = os2ip $ hash m + d = log2 e - log2 n + log2 = ceiling . logBase (2 :: Double) . fromIntegral diff --git a/Crypto/PubKey/ECC/Generate.hs b/Crypto/PubKey/ECC/Generate.hs new file mode 100644 index 0000000..c763d8b --- /dev/null +++ b/Crypto/PubKey/ECC/Generate.hs @@ -0,0 +1,30 @@ +-- | Signature generation. +module Crypto.PubKey.ECC.Generate where + +import Crypto.Random.Types +import Crypto.PubKey.ECC.Types +import Crypto.PubKey.ECC.ECDSA +import Crypto.Number.Generate +import Crypto.PubKey.ECC.Prim + +-- | Generate Q given d. +-- +-- /WARNING:/ Vulnerable to timing attacks. +generateQ :: Curve + -> Integer + -> Point +generateQ curve d = pointMul curve d g + where g = ecc_g $ common_curve curve + +-- | Generate a pair of (private, public) key. +-- +-- /WARNING:/ Vulnerable to timing attacks. +generate :: MonadRandom m + => Curve -- ^ Elliptic Curve + -> m (PublicKey, PrivateKey) +generate curve = do + d <- generateBetween 1 (n - 1) + let q = generateQ curve d + return (PublicKey curve q, PrivateKey curve d) + where + n = ecc_n $ common_curve curve diff --git a/Crypto/PubKey/ECC/Prim.hs b/Crypto/PubKey/ECC/Prim.hs new file mode 100644 index 0000000..ec47bd1 --- /dev/null +++ b/Crypto/PubKey/ECC/Prim.hs @@ -0,0 +1,124 @@ +-- | Elliptic Curve Arithmetic. +-- +-- /WARNING:/ These functions are vulnerable to timing attacks. +module Crypto.PubKey.ECC.Prim + ( pointAdd + , pointDouble + , pointMul + , isPointAtInfinity + , isPointValid + ) where + +import Data.Maybe +import Crypto.Number.ModArithmetic +import Crypto.Number.F2m +import Crypto.PubKey.ECC.Types + +--TODO: Extract helper function for `fromMaybe PointO...` + +-- | Elliptic Curve point addition. +-- +-- /WARNING:/ Vulnerable to timing attacks. +pointAdd :: Curve -> Point -> Point -> Point +pointAdd _ PointO PointO = PointO +pointAdd _ PointO q = q +pointAdd _ p PointO = p +pointAdd c@(CurveFP (CurvePrime pr _)) p@(Point xp yp) q@(Point xq yq) + | p == Point xq (-yq) = PointO + | p == q = pointDouble c p + | otherwise = fromMaybe PointO $ do + s <- divmod (yp - yq) (xp - xq) pr + let xr = (s ^ (2::Int) - xp - xq) `mod` pr + yr = (s * (xp - xr) - yp) `mod` pr + return $ Point xr yr +pointAdd c@(CurveF2m (CurveBinary fx cc)) p@(Point xp yp) q@(Point xq yq) + | p == Point xq (xq `addF2m` yq) = PointO + | p == q = pointDouble c p + | otherwise = fromMaybe PointO $ do + s <- divF2m fx (yp `addF2m` yq) (xp `addF2m` xq) + let xr = mulF2m fx s s `addF2m` s `addF2m` xp `addF2m` xq `addF2m` a + yr = mulF2m fx s (xp `addF2m` xr) `addF2m` xr `addF2m` yp + return $ Point xr yr + where a = ecc_a cc + +-- | Elliptic Curve point doubling. +-- +-- /WARNING:/ Vulnerable to timing attacks. +-- +-- This perform the following calculation: +-- > lambda = (3 * xp ^ 2 + a) / 2 yp +-- > xr = lambda ^ 2 - 2 xp +-- > yr = lambda (xp - xr) - yp +-- +-- With binary curve: +-- > xp == 0 => P = O +-- > otherwise => +-- > s = xp + (yp / xp) +-- > xr = s ^ 2 + s + a +-- > yr = xp ^ 2 + (s+1) * xr +-- +pointDouble :: Curve -> Point -> Point +pointDouble _ PointO = PointO +pointDouble (CurveFP (CurvePrime pr cc)) (Point xp yp) = fromMaybe PointO $ do + lambda <- divmod (3 * xp ^ (2::Int) + a) (2 * yp) pr + let xr = (lambda ^ (2::Int) - 2 * xp) `mod` pr + yr = (lambda * (xp - xr) - yp) `mod` pr + return $ Point xr yr + where a = ecc_a cc +pointDouble (CurveF2m (CurveBinary fx cc)) (Point xp yp) + | xp == 0 = PointO + | otherwise = fromMaybe PointO $ do + s <- return . addF2m xp =<< divF2m fx yp xp + let xr = mulF2m fx s s `addF2m` s `addF2m` a + yr = mulF2m fx xp xp `addF2m` mulF2m fx xr (s `addF2m` 1) + return $ Point xr yr + where a = ecc_a cc + +-- | Elliptic curve point multiplication (double and add algorithm). +-- +-- /WARNING:/ Vulnerable to timing attacks. +pointMul :: Curve -> Integer -> Point -> Point +pointMul _ _ PointO = PointO +pointMul c n p@(Point xp yp) + | n < 0 = pointMul c (-n) (Point xp (-yp)) + | n == 0 = PointO + | n == 1 = p + | odd n = pointAdd c p (pointMul c (n - 1) p) + | otherwise = pointMul c (n `div` 2) (pointDouble c p) + +-- | Check if a point is the point at infinity. +isPointAtInfinity :: Point -> Bool +isPointAtInfinity PointO = True +isPointAtInfinity _ = False + +-- | check if a point is on specific curve +-- +-- This perform three checks: +-- +-- * x is not out of range +-- * y is not out of range +-- * the equation @y^2 = x^3 + a*x + b (mod p)@ holds +isPointValid :: Curve -> Point -> Bool +isPointValid _ PointO = True +isPointValid (CurveFP (CurvePrime p cc)) (Point x y) = + isValid x && isValid y && (y ^ (2 :: Int)) `eqModP` (x ^ (3 :: Int) + a * x + b) + where a = ecc_a cc + b = ecc_b cc + eqModP z1 z2 = (z1 `mod` p) == (z2 `mod` p) + isValid e = e >= 0 && e < p +isPointValid curve@(CurveF2m (CurveBinary fx cc)) pt@(Point x y) = + and [ isValid x + , isValid y + , ((((x `add` a) `mul` x `add` y) `mul` x) `add` b `add` (squareF2m fx y)) == 0 + ] + where a = ecc_a cc + b = ecc_b cc + add = addF2m + mul = mulF2m fx + isValid e = modF2m fx e == e + +-- | div and mod +divmod :: Integer -> Integer -> Integer -> Maybe Integer +divmod y x m = do + i <- inverse (x `mod` m) m + return $ y * i `mod` m diff --git a/Crypto/PubKey/ECC/Types.hs b/Crypto/PubKey/ECC/Types.hs new file mode 100644 index 0000000..16cc996 --- /dev/null +++ b/Crypto/PubKey/ECC/Types.hs @@ -0,0 +1,485 @@ +{-# LANGUAGE DeriveDataTypeable #-} +-- | +-- Module : Crypto.PubKey.ECC.types +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : Experimental +-- Portability : Excellent +-- +-- references: +-- +-- +module Crypto.PubKey.ECC.Types + ( Curve(..) + , Point(..) + , PublicPoint + , PrivateNumber + , CurveBinary(..) + , CurvePrime(..) + , common_curve + , ecc_fx + , ecc_p + , CurveCommon(..) + -- * recommended curves definition + , CurveName(..) + , getCurveByName + ) where + +import Data.Data +import Data.Tuple (swap) + +-- | Define either a binary curve or a prime curve. +data Curve = CurveF2m CurveBinary -- ^ 𝔽(2^m) + | CurveFP CurvePrime -- ^ 𝔽p + deriving (Show,Read,Eq,Data,Typeable) + +-- | ECC Public Point +type PublicPoint = Point + +-- | ECC Private Number +type PrivateNumber = Integer + +-- | Define a point on a curve. +data Point = Point Integer Integer + | PointO -- ^ Point at Infinity + deriving (Show,Read,Eq,Data,Typeable) + +-- | Define an elliptic curve in 𝔽(2^m). +-- The firt parameter is the Integer representatioin of the irreducible polynomial f(x). +data CurveBinary = CurveBinary Integer CurveCommon + deriving (Show,Read,Eq,Data,Typeable) + +-- | Define an elliptic curve in 𝔽p. +-- The first parameter is the Prime Number. +data CurvePrime = CurvePrime Integer CurveCommon + deriving (Show,Read,Eq,Data,Typeable) + +-- | Parameters in common between binary and prime curves. +common_curve :: Curve -> CurveCommon +common_curve (CurveF2m (CurveBinary _ cc)) = cc +common_curve (CurveFP (CurvePrime _ cc)) = cc + +-- | Irreducible polynomial representing the characteristic of a CurveBinary. +ecc_fx :: CurveBinary -> Integer +ecc_fx (CurveBinary fx _) = fx + +-- | Prime number representing the characteristic of a CurvePrime. +ecc_p :: CurvePrime -> Integer +ecc_p (CurvePrime p _) = p + +-- | Define common parameters in a curve definition +-- of the form: y^2 = x^3 + ax + b. +data CurveCommon = CurveCommon + { ecc_a :: Integer -- ^ curve parameter a + , ecc_b :: Integer -- ^ curve parameter b + , ecc_g :: Point -- ^ base point + , ecc_n :: Integer -- ^ order of G + , ecc_h :: Integer -- ^ cofactor + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Define names for known recommended curves. +data CurveName = + SEC_p112r1 + | SEC_p112r2 + | SEC_p128r1 + | SEC_p128r2 + | SEC_p160k1 + | SEC_p160r1 + | SEC_p160r2 + | SEC_p192k1 + | SEC_p192r1 -- aka prime192v1 + | SEC_p224k1 + | SEC_p224r1 + | SEC_p256k1 + | SEC_p256r1 -- aka prime256v1 + | SEC_p384r1 + | SEC_p521r1 + | SEC_t113r1 + | SEC_t113r2 + | SEC_t131r1 + | SEC_t131r2 + | SEC_t163k1 + | SEC_t163r1 + | SEC_t163r2 + | SEC_t193r1 + | SEC_t193r2 + | SEC_t233k1 -- aka NIST K-233 + | SEC_t233r1 + | SEC_t239k1 + | SEC_t283k1 + | SEC_t283r1 + | SEC_t409k1 + | SEC_t409r1 + | SEC_t571k1 + | SEC_t571r1 + deriving (Show,Read,Eq,Ord,Enum,Data,Typeable) + +curvesOIDs :: [ (CurveName, [Integer]) ] +curvesOIDs = + [ (SEC_p112r1, [1,3,132,0,6]) + , (SEC_p112r2, [1,3,132,0,7]) + , (SEC_p128r1, [1,3,132,0,28]) + , (SEC_p128r2, [1,3,132,0,29]) + , (SEC_p160k1, [1,3,132,0,9]) + , (SEC_p160r1, [1,3,132,0,8]) + , (SEC_p160r2, [1,3,132,0,30]) + , (SEC_p192k1, [1,3,132,0,31]) + , (SEC_p192r1, [1,2,840,10045,3,1,1]) + , (SEC_p224k1, [1,3,132,0,32]) + , (SEC_p224r1, [1,3,132,0,33]) + , (SEC_p256k1, [1,3,132,0,10]) + , (SEC_p256r1, [1,2,840,10045,3,1,7]) + , (SEC_p384r1, [1,3,132,0,34]) + , (SEC_p521r1, [1,3,132,0,35]) + , (SEC_t113r1, [1,3,132,0,4]) + , (SEC_t113r2, [1,3,132,0,5]) + , (SEC_t131r1, [1,3,132,0,22]) + , (SEC_t131r2, [1,3,132,0,23]) + , (SEC_t163k1, [1,3,132,0,1]) + , (SEC_t163r1, [1,3,132,0,2]) + , (SEC_t163r2, [1,3,132,0,15]) + , (SEC_t193r1, [1,3,132,0,24]) + , (SEC_t193r2, [1,3,132,0,25]) + , (SEC_t233k1, [1,3,132,0,26]) + , (SEC_t233r1, [1,3,132,0,27]) + , (SEC_t239k1, [1,3,132,0,3]) + , (SEC_t283k1, [1,3,132,0,16]) + , (SEC_t283r1, [1,3,132,0,17]) + , (SEC_t409k1, [1,3,132,0,36]) + , (SEC_t409r1, [1,3,132,0,37]) + , (SEC_t571k1, [1,3,132,0,38]) + , (SEC_t571r1, [1,3,132,0,39]) + ] + +-- | Get the curve definition associated with a recommended known curve name. +getCurveByName :: CurveName -> Curve +getCurveByName SEC_p112r1 = CurveFP $ CurvePrime + 0xdb7c2abf62e35e668076bead208b + (CurveCommon + { ecc_a = 0xdb7c2abf62e35e668076bead2088 + , ecc_b = 0x659ef8ba043916eede8911702b22 + , ecc_g = Point 0x09487239995a5ee76b55f9c2f098 + 0xa89ce5af8724c0a23e0e0ff77500 + , ecc_n = 0xdb7c2abf62e35e7628dfac6561c5 + , ecc_h = 1 + }) +getCurveByName SEC_p112r2 = CurveFP $ CurvePrime + 0xdb7c2abf62e35e668076bead208b + (CurveCommon + { ecc_a = 0x6127c24c05f38a0aaaf65c0ef02c + , ecc_b = 0x51def1815db5ed74fcc34c85d709 + , ecc_g = Point 0x4ba30ab5e892b4e1649dd0928643 + 0xadcd46f5882e3747def36e956e97 + , ecc_n = 0x36df0aafd8b8d7597ca10520d04b + , ecc_h = 4 + }) +getCurveByName SEC_p128r1 = CurveFP $ CurvePrime + 0xfffffffdffffffffffffffffffffffff + (CurveCommon + { ecc_a = 0xfffffffdfffffffffffffffffffffffc + , ecc_b = 0xe87579c11079f43dd824993c2cee5ed3 + , ecc_g = Point 0x161ff7528b899b2d0c28607ca52c5b86 + 0xcf5ac8395bafeb13c02da292dded7a83 + , ecc_n = 0xfffffffe0000000075a30d1b9038a115 + , ecc_h = 1 + }) +getCurveByName SEC_p128r2 = CurveFP $ CurvePrime + 0xfffffffdffffffffffffffffffffffff + (CurveCommon + { ecc_a = 0xd6031998d1b3bbfebf59cc9bbff9aee1 + , ecc_b = 0x5eeefca380d02919dc2c6558bb6d8a5d + , ecc_g = Point 0x7b6aa5d85e572983e6fb32a7cdebc140 + 0x27b6916a894d3aee7106fe805fc34b44 + , ecc_n = 0x3fffffff7fffffffbe0024720613b5a3 + , ecc_h = 4 + }) +getCurveByName SEC_p160k1 = CurveFP $ CurvePrime + 0x00fffffffffffffffffffffffffffffffeffffac73 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000007 + , ecc_g = Point 0x003b4c382ce37aa192a4019e763036f4f5dd4d7ebb + 0x00938cf935318fdced6bc28286531733c3f03c4fee + , ecc_n = 0x0100000000000000000001b8fa16dfab9aca16b6b3 + , ecc_h = 1 + }) +getCurveByName SEC_p160r1 = CurveFP $ CurvePrime + 0x00ffffffffffffffffffffffffffffffff7fffffff + (CurveCommon + { ecc_a = 0x00ffffffffffffffffffffffffffffffff7ffffffc + , ecc_b = 0x001c97befc54bd7a8b65acf89f81d4d4adc565fa45 + , ecc_g = Point 0x004a96b5688ef573284664698968c38bb913cbfc82 + 0x0023a628553168947d59dcc912042351377ac5fb32 + , ecc_n = 0x0100000000000000000001f4c8f927aed3ca752257 + , ecc_h = 1 + }) +getCurveByName SEC_p160r2 = CurveFP $ CurvePrime + 0x00fffffffffffffffffffffffffffffffeffffac73 + (CurveCommon + { ecc_a = 0x00fffffffffffffffffffffffffffffffeffffac70 + , ecc_b = 0x00b4e134d3fb59eb8bab57274904664d5af50388ba + , ecc_g = Point 0x0052dcb034293a117e1f4ff11b30f7199d3144ce6d + 0x00feaffef2e331f296e071fa0df9982cfea7d43f2e + , ecc_n = 0x0100000000000000000000351ee786a818f3a1a16b + , ecc_h = 1 + }) +getCurveByName SEC_p192k1 = CurveFP $ CurvePrime + 0xfffffffffffffffffffffffffffffffffffffffeffffee37 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000000000003 + , ecc_g = Point 0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d + 0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d + , ecc_n = 0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d + , ecc_h = 1 + }) +getCurveByName SEC_p192r1 = CurveFP $ CurvePrime + 0xfffffffffffffffffffffffffffffffeffffffffffffffff + (CurveCommon + { ecc_a = 0xfffffffffffffffffffffffffffffffefffffffffffffffc + , ecc_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 + , ecc_g = Point 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 + 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + , ecc_n = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 + , ecc_h = 1 + }) +getCurveByName SEC_p224k1 = CurveFP $ CurvePrime + 0x00fffffffffffffffffffffffffffffffffffffffffffffffeffffe56d + (CurveCommon + { ecc_a = 0x0000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x0000000000000000000000000000000000000000000000000000000005 + , ecc_g = Point 0x00a1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c + 0x007e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5 + , ecc_n = 0x010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7 + , ecc_h = 1 + }) +getCurveByName SEC_p224r1 = CurveFP $ CurvePrime + 0xffffffffffffffffffffffffffffffff000000000000000000000001 + (CurveCommon + { ecc_a = 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe + , ecc_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 + , ecc_g = Point 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 + 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 + , ecc_n = 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d + , ecc_h = 1 + }) +getCurveByName SEC_p256k1 = CurveFP $ CurvePrime + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + (CurveCommon + { ecc_a = 0x0000000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x0000000000000000000000000000000000000000000000000000000000000007 + , ecc_g = Point 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 + 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 + , ecc_n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 + , ecc_h = 1 + }) +getCurveByName SEC_p256r1 = CurveFP $ CurvePrime + 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff + (CurveCommon + { ecc_a = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc + , ecc_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b + , ecc_g = Point 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 + 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 + , ecc_n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 + , ecc_h = 1 + }) +getCurveByName SEC_p384r1 = CurveFP $ CurvePrime + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff + (CurveCommon + { ecc_a = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc + , ecc_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef + , ecc_g = Point 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 + 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f + , ecc_n = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 + , ecc_h = 1 + }) +getCurveByName SEC_p521r1 = CurveFP $ CurvePrime + 0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + (CurveCommon + { ecc_a = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc + , ecc_b = 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 + , ecc_g = Point 0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 + 0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 + , ecc_n = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409 + , ecc_h = 1 + }) +getCurveByName SEC_t113r1 = CurveF2m $ CurveBinary + 0x020000000000000000000000000201 + (CurveCommon + { ecc_a = 0x003088250ca6e7c7fe649ce85820f7 + , ecc_b = 0x00e8bee4d3e2260744188be0e9c723 + , ecc_g = Point 0x009d73616f35f4ab1407d73562c10f + 0x00a52830277958ee84d1315ed31886 + , ecc_n = 0x0100000000000000d9ccec8a39e56f + , ecc_h = 2 + }) +getCurveByName SEC_t113r2 = CurveF2m $ CurveBinary + 0x020000000000000000000000000201 + (CurveCommon + { ecc_a = 0x00689918dbec7e5a0dd6dfc0aa55c7 + , ecc_b = 0x0095e9a9ec9b297bd4bf36e059184f + , ecc_g = Point 0x01a57a6a7b26ca5ef52fcdb8164797 + 0x00b3adc94ed1fe674c06e695baba1d + , ecc_n = 0x010000000000000108789b2496af93 + , ecc_h = 2 + }) +getCurveByName SEC_t131r1 = CurveF2m $ CurveBinary + 0x080000000000000000000000000000010d + (CurveCommon + { ecc_a = 0x07a11b09a76b562144418ff3ff8c2570b8 + , ecc_b = 0x0217c05610884b63b9c6c7291678f9d341 + , ecc_g = Point 0x0081baf91fdf9833c40f9c181343638399 + 0x078c6e7ea38c001f73c8134b1b4ef9e150 + , ecc_n = 0x0400000000000000023123953a9464b54d + , ecc_h = 2 + }) +getCurveByName SEC_t131r2 = CurveF2m $ CurveBinary + 0x080000000000000000000000000000010d + (CurveCommon + { ecc_a = 0x03e5a88919d7cafcbf415f07c2176573b2 + , ecc_b = 0x04b8266a46c55657ac734ce38f018f2192 + , ecc_g = Point 0x0356dcd8f2f95031ad652d23951bb366a8 + 0x0648f06d867940a5366d9e265de9eb240f + , ecc_n = 0x0400000000000000016954a233049ba98f + , ecc_h = 2 + }) +getCurveByName SEC_t163k1 = CurveF2m $ CurveBinary + 0x0800000000000000000000000000000000000000c9 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000001 + , ecc_b = 0x000000000000000000000000000000000000000001 + , ecc_g = Point 0x02fe13c0537bbc11acaa07d793de4e6d5e5c94eee8 + 0x0289070fb05d38ff58321f2e800536d538ccdaa3d9 + , ecc_n = 0x04000000000000000000020108a2e0cc0d99f8a5ef + , ecc_h = 2 + }) +getCurveByName SEC_t163r1 = CurveF2m $ CurveBinary + 0x0800000000000000000000000000000000000000c9 + (CurveCommon + { ecc_a = 0x07b6882caaefa84f9554ff8428bd88e246d2782ae2 + , ecc_b = 0x0713612dcddcb40aab946bda29ca91f73af958afd9 + , ecc_g = Point 0x0369979697ab43897789566789567f787a7876a654 + 0x00435edb42efafb2989d51fefce3c80988f41ff883 + , ecc_n = 0x03ffffffffffffffffffff48aab689c29ca710279b + , ecc_h = 2 + }) +getCurveByName SEC_t163r2 = CurveF2m $ CurveBinary + 0x0800000000000000000000000000000000000000c9 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000001 + , ecc_b = 0x020a601907b8c953ca1481eb10512f78744a3205fd + , ecc_g = Point 0x03f0eba16286a2d57ea0991168d4994637e8343e36 + 0x00d51fbc6c71a0094fa2cdd545b11c5c0c797324f1 + , ecc_n = 0x040000000000000000000292fe77e70c12a4234c33 + , ecc_h = 2 + }) +getCurveByName SEC_t193r1 = CurveF2m $ CurveBinary + 0x02000000000000000000000000000000000000000000008001 + (CurveCommon + { ecc_a = 0x0017858feb7a98975169e171f77b4087de098ac8a911df7b01 + , ecc_b = 0x00fdfb49bfe6c3a89facadaa7a1e5bbc7cc1c2e5d831478814 + , ecc_g = Point 0x01f481bc5f0ff84a74ad6cdf6fdef4bf6179625372d8c0c5e1 + 0x0025e399f2903712ccf3ea9e3a1ad17fb0b3201b6af7ce1b05 + , ecc_n = 0x01000000000000000000000000c7f34a778f443acc920eba49 + , ecc_h = 2 + }) +getCurveByName SEC_t193r2 = CurveF2m $ CurveBinary + 0x02000000000000000000000000000000000000000000008001 + (CurveCommon + { ecc_a = 0x0163f35a5137c2ce3ea6ed8667190b0bc43ecd69977702709b + , ecc_b = 0x00c9bb9e8927d4d64c377e2ab2856a5b16e3efb7f61d4316ae + , ecc_g = Point 0x00d9b67d192e0367c803f39e1a7e82ca14a651350aae617e8f + 0x01ce94335607c304ac29e7defbd9ca01f596f927224cdecf6c + , ecc_n = 0x010000000000000000000000015aab561b005413ccd4ee99d5 + , ecc_h = 2 + }) +getCurveByName SEC_t233k1 = CurveF2m $ CurveBinary + 0x020000000000000000000000000000000000000004000000000000000001 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000000000000000000000001 + , ecc_g = Point 0x017232ba853a7e731af129f22ff4149563a419c26bf50a4c9d6eefad6126 + 0x01db537dece819b7f70f555a67c427a8cd9bf18aeb9b56e0c11056fae6a3 + , ecc_n = 0x008000000000000000000000000000069d5bb915bcd46efb1ad5f173abdf + , ecc_h = 4 + }) +getCurveByName SEC_t233r1 = CurveF2m $ CurveBinary + 0x020000000000000000000000000000000000000004000000000000000001 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000001 + , ecc_b = 0x0066647ede6c332c7f8c0923bb58213b333b20e9ce4281fe115f7d8f90ad + , ecc_g = Point 0x00fac9dfcbac8313bb2139f1bb755fef65bc391f8b36f8f8eb7371fd558b + 0x01006a08a41903350678e58528bebf8a0beff867a7ca36716f7e01f81052 + , ecc_n = 0x01000000000000000000000000000013e974e72f8a6922031d2603cfe0d7 + , ecc_h = 2 + }) +getCurveByName SEC_t239k1 = CurveF2m $ CurveBinary + 0x800000000000000000004000000000000000000000000000000000000001 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000000000000000000000001 + , ecc_g = Point 0x29a0b6a887a983e9730988a68727a8b2d126c44cc2cc7b2a6555193035dc + 0x76310804f12e549bdb011c103089e73510acb275fc312a5dc6b76553f0ca + , ecc_n = 0x2000000000000000000000000000005a79fec67cb6e91f1c1da800e478a5 + , ecc_h = 4 + }) +getCurveByName SEC_t283k1 = CurveF2m $ CurveBinary + 0x0800000000000000000000000000000000000000000000000000000000000000000010a1 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_g = Point 0x0503213f78ca44883f1a3b8162f188e553cd265f23c1567a16876913b0c2ac2458492836 + 0x01ccda380f1c9e318d90f95d07e5426fe87e45c0e8184698e45962364e34116177dd2259 + , ecc_n = 0x01ffffffffffffffffffffffffffffffffffe9ae2ed07577265dff7f94451e061e163c61 + , ecc_h = 4 + }) +getCurveByName SEC_t283r1 = CurveF2m $ CurveBinary + 0x0800000000000000000000000000000000000000000000000000000000000000000010a1 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_b = 0x027b680ac8b8596da5a4af8a19a0303fca97fd7645309fa2a581485af6263e313b79a2f5 + , ecc_g = Point 0x05f939258db7dd90e1934f8c70b0dfec2eed25b8557eac9c80e2e198f8cdbecd86b12053 + 0x03676854fe24141cb98fe6d4b20d02b4516ff702350eddb0826779c813f0df45be8112f4 + , ecc_n = 0x03ffffffffffffffffffffffffffffffffffef90399660fc938a90165b042a7cefadb307 + , ecc_h = 2 + }) +getCurveByName SEC_t409k1 = CurveF2m $ CurveBinary + 0x02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001 + (CurveCommon + { ecc_a = 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_g = Point 0x0060f05f658f49c1ad3ab1890f7184210efd0987e307c84c27accfb8f9f67cc2c460189eb5aaaa62ee222eb1b35540cfe9023746 + 0x01e369050b7c4e42acba1dacbf04299c3460782f918ea427e6325165e9ea10e3da5f6c42e9c55215aa9ca27a5863ec48d8e0286b + , ecc_n = 0x007ffffffffffffffffffffffffffffffffffffffffffffffffffe5f83b2d4ea20400ec4557d5ed3e3e7ca5b4b5c83b8e01e5fcf + , ecc_h = 4 + }) +getCurveByName SEC_t409r1 = CurveF2m $ CurveBinary + 0x02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001 + (CurveCommon + { ecc_a = 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_b = 0x0021a5c2c8ee9feb5c4b9a753b7b476b7fd6422ef1f3dd674761fa99d6ac27c8a9a197b272822f6cd57a55aa4f50ae317b13545f + , ecc_g = Point 0x015d4860d088ddb3496b0c6064756260441cde4af1771d4db01ffe5b34e59703dc255a868a1180515603aeab60794e54bb7996a7 + 0x0061b1cfab6be5f32bbfa78324ed106a7636b9c5a7bd198d0158aa4f5488d08f38514f1fdf4b4f40d2181b3681c364ba0273c706 + , ecc_n = 0x010000000000000000000000000000000000000000000000000001e2aad6a612f33307be5fa47c3c9e052f838164cd37d9a21173 + , ecc_h = 2 + }) +getCurveByName SEC_t571k1 = CurveF2m $ CurveBinary + 0x080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + , ecc_b = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_g = Point 0x026eb7a859923fbc82189631f8103fe4ac9ca2970012d5d46024804801841ca44370958493b205e647da304db4ceb08cbbd1ba39494776fb988b47174dca88c7e2945283a01c8972 + 0x0349dc807f4fbf374f4aeade3bca95314dd58cec9f307a54ffc61efc006d8a2c9d4979c0ac44aea74fbebbb9f772aedcb620b01a7ba7af1b320430c8591984f601cd4c143ef1c7a3 + , ecc_n = 0x020000000000000000000000000000000000000000000000000000000000000000000000131850e1f19a63e4b391a8db917f4138b630d84be5d639381e91deb45cfe778f637c1001 + , ecc_h = 4 + }) +getCurveByName SEC_t571r1 = CurveF2m $ CurveBinary + 0x080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425 + (CurveCommon + { ecc_a = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + , ecc_b = 0x02f40e7e2221f295de297117b7f3d62f5c6a97ffcb8ceff1cd6ba8ce4a9a18ad84ffabbd8efa59332be7ad6756a66e294afd185a78ff12aa520e4de739baca0c7ffeff7f2955727a + , ecc_g = Point 0x0303001d34b856296c16c0d40d3cd7750a93d1d2955fa80aa5f40fc8db7b2abdbde53950f4c0d293cdd711a35b67fb1499ae60038614f1394abfa3b4c850d927e1e7769c8eec2d19 + 0x037bf27342da639b6dccfffeb73d69d78c6c27a6009cbbca1980f8533921e8a684423e43bab08a576291af8f461bb2a8b3531d2f0485c19b16e2f1516e23dd3c1a4827af1b8ac15b + , ecc_n = 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe661ce18ff55987308059b186823851ec7dd9ca1161de93d5174d66e8382e9bb2fe84e47 + , ecc_h = 2 + }) diff --git a/Crypto/PubKey/ElGamal.hs b/Crypto/PubKey/ElGamal.hs new file mode 100644 index 0000000..d699c92 --- /dev/null +++ b/Crypto/PubKey/ElGamal.hs @@ -0,0 +1,143 @@ +-- | +-- Module : Crypto.PubKey.ElGamal +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +-- This module is a work in progress. do not use: +-- it might eat your dog, your data or even both. +-- +-- TODO: provide a mapping between integer and ciphertext +-- generate numbers correctly +-- +module Crypto.PubKey.ElGamal + ( Params + , PublicNumber + , PrivateNumber + , EphemeralKey(..) + , SharedKey + , Signature + -- * generation + , generatePrivate + , generatePublic + -- * encryption and decryption with no scheme + , encryptWith + , encrypt + , decrypt + -- * signature primitives + , signWith + , sign + -- * verification primitives + , verify + ) where + +import Control.Applicative +import Data.Maybe (fromJust) +import Data.ByteString (ByteString) +import Crypto.Number.ModArithmetic (expSafe, expFast, inverse) +import Crypto.Number.Generate (generateMax) +import Crypto.Number.Serialize (os2ip) +import Crypto.Number.Basic (gcde_binary) +import Crypto.Random.Types +import Crypto.PubKey.HashDescr (HashFunction) +import Crypto.PubKey.DH (PrivateNumber(..), PublicNumber(..), Params(..), SharedKey(..)) + +-- | ElGamal Signature +data Signature = Signature (Integer, Integer) + +-- | ElGamal Ephemeral key. also called Temporary key. +newtype EphemeralKey = EphemeralKey Integer + +-- | generate a private number with no specific property +-- this number is usually called a and need to be between +-- 0 and q (order of the group G). +-- +generatePrivate :: MonadRandom m => Integer -> m PrivateNumber +generatePrivate q = PrivateNumber <$> generateMax q + +-- | generate an ephemeral key which is a number with no specific property, +-- and need to be between 0 and q (order of the group G). +-- +generateEphemeral :: MonadRandom m => Integer -> m EphemeralKey +generateEphemeral q = toEphemeral <$> generatePrivate q + where toEphemeral (PrivateNumber n) = EphemeralKey n + +-- | generate a public number that is for the other party benefits. +-- this number is usually called h=g^a +generatePublic :: Params -> PrivateNumber -> PublicNumber +generatePublic (Params p g) (PrivateNumber a) = PublicNumber $ expSafe g a p + +-- | encrypt with a specified ephemeral key +-- do not reuse ephemeral key. +encryptWith :: EphemeralKey -> Params -> PublicNumber -> Integer -> (Integer,Integer) +encryptWith (EphemeralKey b) (Params p g) (PublicNumber h) m = (c1,c2) + where s = expSafe h b p + c1 = expSafe g b p + c2 = (s * m) `mod` p + +-- | encrypt a message using params and public keys +-- will generate b (called the ephemeral key) +encrypt :: MonadRandom m => Params -> PublicNumber -> Integer -> m (Integer,Integer) +encrypt params@(Params p _) public m = (\b -> encryptWith b params public m) <$> generateEphemeral q + where q = p-1 -- p is prime, hence order of the group is p-1 + +-- | decrypt message +decrypt :: Params -> PrivateNumber -> (Integer, Integer) -> Integer +decrypt (Params p _) (PrivateNumber a) (c1,c2) = (c2 * sm1) `mod` p + where s = expSafe c1 a p + sm1 = fromJust $ inverse s p -- always inversible in Zp + +-- | sign a message with an explicit k number +-- +-- if k is not appropriate, then no signature is returned. +-- +-- with some appropriate value of k, the signature generation can fail, +-- and no signature is returned. User of this function need to retry +-- with a different k value. +signWith :: Integer -- ^ random number k, between 0 and p-1 and gcd(k,p-1)=1 + -> Params -- ^ DH params (p,g) + -> PrivateNumber -- ^ DH private key + -> HashFunction -- ^ collision resistant hash function + -> ByteString -- ^ message to sign + -> Maybe Signature +signWith k (Params p g) (PrivateNumber x) hashF msg + | k >= p-1 || d > 1 = Nothing -- gcd(k,p-1) is not 1 + | s == 0 = Nothing + | otherwise = Just $ Signature (r,s) + where r = expSafe g k p + h = os2ip $ hashF msg + s = ((h - x*r) * kInv) `mod` (p-1) + (kInv,_,d) = gcde_binary k (p-1) + +-- | sign message +-- +-- This function will generate a random number, however +-- as the signature might fail, the function will automatically retry +-- until a proper signature has been created. +-- +sign :: MonadRandom m + => Params -- ^ DH params (p,g) + -> PrivateNumber -- ^ DH private key + -> HashFunction -- ^ collision resistant hash function + -> ByteString -- ^ message to sign + -> m Signature +sign params@(Params p _) priv hashF msg = do + k <- generateMax (p-1) + case signWith k params priv hashF msg of + Nothing -> sign params priv hashF msg + Just sig -> return sig + +-- | verify a signature +verify :: Params + -> PublicNumber + -> HashFunction + -> ByteString + -> Signature + -> Bool +verify (Params p g) (PublicNumber y) hashF msg (Signature (r,s)) + | or [r <= 0,r >= p,s <= 0,s >= (p-1)] = False + | otherwise = lhs == rhs + where h = os2ip $ hashF msg + lhs = expFast g h p + rhs = (expFast y r p * expFast r s p) `mod` p diff --git a/Crypto/PubKey/HashDescr.hs b/Crypto/PubKey/HashDescr.hs new file mode 100644 index 0000000..26d9eea --- /dev/null +++ b/Crypto/PubKey/HashDescr.hs @@ -0,0 +1,102 @@ +{-# LANGUAGE OverloadedStrings #-} +-- | +-- Module : Crypto.PubKey.HashDescr +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +-- Standard digests wrapped in ASN1 structure +-- +module Crypto.PubKey.HashDescr + ( + -- * Types + HashFunction + , HashDescr(..) + -- * List of known hash description + , hashDescrMD2 + , hashDescrMD5 + , hashDescrSHA1 + , hashDescrSHA224 + , hashDescrSHA256 + , hashDescrSHA384 + , hashDescrSHA512 + , hashDescrRIPEMD160 + ) where + +import Data.ByteString (ByteString) +import Data.Byteable (toBytes) +import qualified Data.ByteString as B +import Crypto.Hash + +-- | A standard hash function returning a digest object +type HashFunction = ByteString -> ByteString + +-- | Describe a hash function and a way to wrap the digest into +-- an DER encoded ASN1 marshalled structure. +data HashDescr = HashDescr { hashFunction :: HashFunction -- ^ hash function + , digestToASN1 :: ByteString -> ByteString -- ^ convertion to an ASN1 wrapped digest bytestring + } + +-- | Describe the MD2 hashing algorithm +hashDescrMD2 :: HashDescr +hashDescrMD2 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest MD2) + , digestToASN1 = toHashWithInfo "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10" + } +-- | Describe the MD5 hashing algorithm +hashDescrMD5 :: HashDescr +hashDescrMD5 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest MD5) + , digestToASN1 = toHashWithInfo "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10" + } +-- | Describe the SHA1 hashing algorithm +hashDescrSHA1 :: HashDescr +hashDescrSHA1 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest SHA1) + , digestToASN1 = toHashWithInfo "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14" + } +-- | Describe the SHA224 hashing algorithm +hashDescrSHA224 :: HashDescr +hashDescrSHA224 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest SHA224) + , digestToASN1 = toHashWithInfo "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c" + } +-- | Describe the SHA256 hashing algorithm +hashDescrSHA256 :: HashDescr +hashDescrSHA256 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest SHA256) + , digestToASN1 = toHashWithInfo "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20" + } +-- | Describe the SHA384 hashing algorithm +hashDescrSHA384 :: HashDescr +hashDescrSHA384 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest SHA384) + , digestToASN1 = toHashWithInfo "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30" + } +-- | Describe the SHA512 hashing algorithm +hashDescrSHA512 :: HashDescr +hashDescrSHA512 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest SHA512) + , digestToASN1 = toHashWithInfo "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40" + } + +-- | Describe the RIPEMD160 hashing algorithm +hashDescrRIPEMD160 :: HashDescr +hashDescrRIPEMD160 = + HashDescr { hashFunction = toBytes . (hash :: ByteString -> Digest RIPEMD160) + , digestToASN1 = toHashWithInfo "\x30\x21\x30\x09\x06\x05\x2b\x24\x03\x02\x01\x05\x00\x04\x14" + } + +-- | Generate the marshalled structure with the following ASN1 structure: +-- +-- Start Sequence +-- ,Start Sequence +-- ,OID oid +-- ,Null +-- ,End Sequence +-- ,OctetString digest +-- ,End Sequence +-- +toHashWithInfo :: ByteString -> ByteString -> ByteString +toHashWithInfo pre digest = pre `B.append` digest diff --git a/Crypto/PubKey/Internal.hs b/Crypto/PubKey/Internal.hs new file mode 100644 index 0000000..d0be813 --- /dev/null +++ b/Crypto/PubKey/Internal.hs @@ -0,0 +1,24 @@ +-- | +-- Module : Crypto.PubKey.Internal +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +module Crypto.PubKey.Internal + ( and' + , (&&!) + ) where + +import Data.List (foldl') + +-- | This is a strict version of and +and' :: [Bool] -> Bool +and' l = foldl' (&&!) True l + +-- | This is a strict version of &&. +(&&!) :: Bool -> Bool -> Bool +True &&! True = True +True &&! False = False +False &&! True = False +False &&! False = False diff --git a/Crypto/PubKey/MaskGenFunction.hs b/Crypto/PubKey/MaskGenFunction.hs new file mode 100644 index 0000000..c06cde8 --- /dev/null +++ b/Crypto/PubKey/MaskGenFunction.hs @@ -0,0 +1,31 @@ +-- | +-- Module : Crypto.PubKey.MaskGenFunction +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +module Crypto.PubKey.MaskGenFunction + ( MaskGenAlgorithm + , mgf1 + ) where + +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import Crypto.PubKey.HashDescr +import Crypto.Number.Serialize (i2ospOf_) + +-- | Represent a mask generation algorithm +type MaskGenAlgorithm = HashFunction -- ^ hash function to use + -> ByteString -- ^ seed + -> Int -- ^ length to generate + -> ByteString + +-- | Mask generation algorithm MGF1 +mgf1 :: MaskGenAlgorithm +mgf1 hashF seed len = loop B.empty 0 + where loop t counter + | B.length t >= len = B.take len t + | otherwise = let counterBS = i2ospOf_ 4 counter + newT = t `B.append` hashF (seed `B.append` counterBS) + in loop newT (counter+1) diff --git a/Crypto/PubKey/RSA.hs b/Crypto/PubKey/RSA.hs new file mode 100644 index 0000000..f2749e3 --- /dev/null +++ b/Crypto/PubKey/RSA.hs @@ -0,0 +1,109 @@ +-- | +-- Module : Crypto.PubKey.RSA +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +module Crypto.PubKey.RSA + ( Error(..) + , PublicKey(..) + , PrivateKey(..) + , Blinder(..) + -- * generation function + , generateWith + , generate + , generateBlinder + ) where + +import Data.Bits +import Data.Word +import Control.Applicative +import Crypto.Random.Types +import Crypto.Number.ModArithmetic (inverse, inverseCoprimes) +import Crypto.Number.Generate (generateMax) +import Crypto.Number.Prime (generatePrime) +import Crypto.PubKey.RSA.Types + +-- some bad implementation will not serialize ASN.1 integer properly, leading +-- to negative modulus. +-- TODO : Find a better place for this +toPositive :: Integer -> Integer +toPositive int + | int < 0 = uintOfBytes $ bytesOfInt int + | otherwise = int + where uintOfBytes = foldl (\acc n -> (acc `shiftL` 8) + fromIntegral n) 0 + bytesOfInt :: Integer -> [Word8] + bytesOfInt n = if testBit (head nints) 7 then nints else 0xff : nints + where nints = reverse $ plusOne $ reverse $ map complement $ bytesOfUInt (abs n) + plusOne [] = [1] + plusOne (x:xs) = if x == 0xff then 0 : plusOne xs else (x+1) : xs + bytesOfUInt x = reverse (list x) + where list i = if i <= 0xff then [fromIntegral i] else (fromIntegral i .&. 0xff) : list (i `shiftR` 8) + +-- | Generate a key pair given p and q. +-- +-- p and q need to be distinct prime numbers. +-- +-- e need to be coprime to phi=(p-1)*(q-1). If that's not the +-- case, the function will not return a key pair. +-- A small hamming weight results in better performance. +-- +-- * e=0x10001 is a popular choice +-- +-- * e=3 is popular as well, but proven to not be as secure for some cases. +-- +generateWith :: (Integer, Integer) -- ^ chosen distinct primes p and q + -> Int -- ^ size in bytes + -> Integer -- ^ RSA public exponant 'e' + -> Maybe (PublicKey, PrivateKey) +generateWith (p,q) size e = + case inverse e phi of + Nothing -> Nothing + Just d -> Just (pub,priv d) + where n = p*q + phi = (p-1)*(q-1) + -- q and p should be *distinct* *prime* numbers, hence always coprime + qinv = inverseCoprimes q p + pub = PublicKey { public_size = size + , public_n = n + , public_e = e + } + priv d = PrivateKey { private_pub = pub + , private_d = d + , private_p = p + , private_q = q + , private_dP = d `mod` (p-1) + , private_dQ = d `mod` (q-1) + , private_qinv = qinv + } + +-- | generate a pair of (private, public) key of size in bytes. +generate :: MonadRandom m + => Int -- ^ size in bytes + -> Integer -- ^ RSA public exponant 'e' + -> m (PublicKey, PrivateKey) +generate size e = loop + where + loop = do -- loop until we find a valid key pair given e + pq <- generatePQ + case generateWith pq size e of + Nothing -> loop + Just pp -> return pp + generatePQ = do + p <- generatePrime (8 * (size `div` 2)) + q <- generateQ p + return (p,q) + generateQ p = do + q <- generatePrime (8 * (size - (size `div` 2))) + if p == q then generateQ p else return q + +-- | Generate a blinder to use with decryption and signing operation +-- +-- the unique parameter apart from the random number generator is the +-- public key value N. +generateBlinder :: MonadRandom m + => Integer -- ^ RSA public N parameter. + -> m Blinder +generateBlinder n = + (\r -> Blinder r (inverseCoprimes r n)) <$> generateMax n diff --git a/Crypto/PubKey/RSA/OAEP.hs b/Crypto/PubKey/RSA/OAEP.hs new file mode 100644 index 0000000..fe742cb --- /dev/null +++ b/Crypto/PubKey/RSA/OAEP.hs @@ -0,0 +1,151 @@ +-- | +-- Module : Crypto.PubKey.RSA.OAEP +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +-- RSA OAEP mode +-- +-- +{-# LANGUAGE OverloadedStrings #-} +module Crypto.PubKey.RSA.OAEP + ( + OAEPParams(..) + , defaultOAEPParams + -- * OAEP encryption + , encryptWithSeed + , encrypt + -- * OAEP decryption + , decrypt + , decryptSafer + ) where + +import Crypto.Random.Types +import Crypto.PubKey.RSA.Types +import Crypto.PubKey.HashDescr +import Crypto.PubKey.MaskGenFunction +import Crypto.PubKey.RSA.Prim +import Crypto.PubKey.RSA (generateBlinder) +import Crypto.PubKey.Internal (and') +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import Data.Bits (xor) + +-- | Parameters for OAEP encryption/decryption +data OAEPParams = OAEPParams + { oaepHash :: HashFunction -- ^ Hash function to use. + , oaepMaskGenAlg :: MaskGenAlgorithm -- ^ Mask Gen algorithm to use. + , oaepLabel :: Maybe ByteString -- ^ Optional label prepended to message. + } + +-- | Default Params with a specified hash function +defaultOAEPParams :: HashFunction -> OAEPParams +defaultOAEPParams hashF = + OAEPParams { oaepHash = hashF + , oaepMaskGenAlg = mgf1 + , oaepLabel = Nothing + } + +-- | Encrypt a message using OAEP with a predefined seed. +encryptWithSeed :: ByteString -- ^ Seed + -> OAEPParams -- ^ OAEP params to use for encryption + -> PublicKey -- ^ Public key. + -> ByteString -- ^ Message to encrypt + -> Either Error ByteString +encryptWithSeed seed oaep pk msg + | k < 2*hashLen+2 = Left InvalidParameters + | B.length seed /= hashLen = Left InvalidParameters + | mLen > k - 2*hashLen-2 = Left MessageTooLong + | otherwise = Right $ ep pk em + where -- parameters + k = public_size pk + mLen = B.length msg + hashF = oaepHash oaep + mgf = (oaepMaskGenAlg oaep) hashF + labelHash = hashF $ maybe B.empty id $ oaepLabel oaep + hashLen = B.length labelHash + + -- put fields + ps = B.replicate (k - mLen - 2*hashLen - 2) 0 + db = B.concat [labelHash, ps, B.singleton 0x1, msg] + dbmask = mgf seed (k - hashLen - 1) + maskedDB = B.pack $ B.zipWith xor db dbmask + seedMask = mgf maskedDB hashLen + maskedSeed = B.pack $ B.zipWith xor seed seedMask + em = B.concat [B.singleton 0x0,maskedSeed,maskedDB] + +-- | Encrypt a message using OAEP +encrypt :: MonadRandom m + => OAEPParams -- ^ OAEP params to use for encryption. + -> PublicKey -- ^ Public key. + -> ByteString -- ^ Message to encrypt + -> m (Either Error ByteString) +encrypt oaep pk msg = do + seed <- getRandomBytes hashLen + return (encryptWithSeed seed oaep pk msg) + where + hashF = oaepHash oaep + hashLen = B.length (hashF B.empty) + +-- | un-pad a OAEP encoded message. +-- +-- It doesn't apply the RSA decryption primitive +unpad :: OAEPParams -- ^ OAEP params to use + -> Int -- ^ size of the key in bytes + -> ByteString -- ^ encoded message (not encrypted) + -> Either Error ByteString +unpad oaep k em + | paddingSuccess = Right msg + | otherwise = Left MessageNotRecognized + where -- parameters + hashF = oaepHash oaep + mgf = (oaepMaskGenAlg oaep) hashF + labelHash = hashF $ maybe B.empty id $ oaepLabel oaep + hashLen = B.length labelHash + -- getting em's fields + (pb, em0) = B.splitAt 1 em + (maskedSeed,maskedDB) = B.splitAt hashLen em0 + seedMask = mgf maskedDB hashLen + seed = B.pack $ B.zipWith xor maskedSeed seedMask + dbmask = mgf seed (k - hashLen - 1) + db = B.pack $ B.zipWith xor maskedDB dbmask + -- getting db's fields + (labelHash',db1) = B.splitAt hashLen db + (_,db2) = B.break (/= 0) db1 + (ps1,msg) = B.splitAt 1 db2 + + paddingSuccess = and' [ labelHash' == labelHash -- no need for constant eq + , ps1 == "\x01" + , pb == "\x00" + ] + +-- | Decrypt a ciphertext using OAEP +-- +-- When the signature is not in a context where an attacker could gain +-- information from the timing of the operation, the blinder can be set to None. +-- +-- If unsure always set a blinder or use decryptSafer +decrypt :: Maybe Blinder -- ^ Optional blinder + -> OAEPParams -- ^ OAEP params to use for decryption + -> PrivateKey -- ^ Private key + -> ByteString -- ^ Cipher text + -> Either Error ByteString +decrypt blinder oaep pk cipher + | B.length cipher /= k = Left MessageSizeIncorrect + | k < 2*hashLen+2 = Left InvalidParameters + | otherwise = unpad oaep (private_size pk) $ dp blinder pk cipher + where -- parameters + k = private_size pk + hashF = oaepHash oaep + hashLen = B.length (hashF B.empty) + +-- | Decrypt a ciphertext using OAEP and by automatically generating a blinder. +decryptSafer :: MonadRandom m + => OAEPParams -- ^ OAEP params to use for decryption + -> PrivateKey -- ^ Private key + -> ByteString -- ^ Cipher text + -> m (Either Error ByteString) +decryptSafer oaep pk cipher = do + blinder <- generateBlinder (private_n pk) + return (decrypt (Just blinder) oaep pk cipher) diff --git a/Crypto/PubKey/RSA/PKCS15.hs b/Crypto/PubKey/RSA/PKCS15.hs new file mode 100644 index 0000000..29cbfe0 --- /dev/null +++ b/Crypto/PubKey/RSA/PKCS15.hs @@ -0,0 +1,142 @@ +-- | +-- Module : Crypto.PubKey.RSA.PKCS15 +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +{-# LANGUAGE OverloadedStrings #-} +module Crypto.PubKey.RSA.PKCS15 + ( + -- * padding and unpadding + pad + , padSignature + , unpad + -- * private key operations + , decrypt + , decryptSafer + , sign + , signSafer + -- * public key operations + , encrypt + , verify + ) where + +import Crypto.Random.Types +import Crypto.PubKey.Internal (and') +import Crypto.PubKey.RSA.Types +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import Crypto.PubKey.RSA.Prim +import Crypto.PubKey.RSA (generateBlinder) +import Crypto.PubKey.HashDescr + +-- | This produce a standard PKCS1.5 padding for encryption +pad :: MonadRandom m => Int -> ByteString -> m (Either Error ByteString) +pad len m + | B.length m > len - 11 = return (Left MessageTooLong) + | otherwise = do + padding <- getNonNullRandom (len - B.length m - 3) + return $ Right $ B.concat [ B.singleton 0, B.singleton 2, padding, B.singleton 0, m ] + + where {- get random non-null bytes -} + getNonNullRandom :: MonadRandom m => Int -> m ByteString + getNonNullRandom n = do + bs0 <- getRandomBytes n + let bytes = B.pack $ filter (/= 0) $ B.unpack $ bs0 + left = n - B.length bytes + if left == 0 + then return bytes + else do bend <- getNonNullRandom left + return (bytes `B.append` bend) + +-- | Produce a standard PKCS1.5 padding for signature +padSignature :: Int -> ByteString -> Either Error ByteString +padSignature klen signature + | klen < siglen+1 = Left SignatureTooLong + | otherwise = Right $ B.concat [B.singleton 0,B.singleton 1,padding,B.singleton 0,signature] + where + siglen = B.length signature + padding = B.replicate (klen - siglen - 3) 0xff + +-- | Try to remove a standard PKCS1.5 encryption padding. +unpad :: ByteString -> Either Error ByteString +unpad packed + | paddingSuccess = Right m + | otherwise = Left MessageNotRecognized + where + (zt, ps0m) = B.splitAt 2 packed + (ps, zm) = B.span (/= 0) ps0m + (z, m) = B.splitAt 1 zm + paddingSuccess = and' [ zt == "\x00\x02" + , z == "\x00" + , B.length ps >= 8 + ] + +-- | decrypt message using the private key. +-- +-- When the decryption is not in a context where an attacker could gain +-- information from the timing of the operation, the blinder can be set to None. +-- +-- If unsure always set a blinder or use decryptSafer +decrypt :: Maybe Blinder -- ^ optional blinder + -> PrivateKey -- ^ RSA private key + -> ByteString -- ^ cipher text + -> Either Error ByteString +decrypt blinder pk c + | B.length c /= (private_size pk) = Left MessageSizeIncorrect + | otherwise = unpad $ dp blinder pk c + +-- | decrypt message using the private key and by automatically generating a blinder. +decryptSafer :: MonadRandom m + => PrivateKey -- ^ RSA private key + -> ByteString -- ^ cipher text + -> m (Either Error ByteString) +decryptSafer pk b = do + blinder <- generateBlinder (private_n pk) + return (decrypt (Just blinder) pk b) + +-- | encrypt a bytestring using the public key and a CPRG random generator. +-- +-- the message need to be smaller than the key size - 11 +encrypt :: MonadRandom m => PublicKey -> ByteString -> m (Either Error ByteString) +encrypt pk m = do + r <- pad (public_size pk) m + case r of + Left err -> return $ Left err + Right em -> return $ Right (ep pk em) + +-- | sign message using private key, a hash and its ASN1 description +-- +-- When the signature is not in a context where an attacker could gain +-- information from the timing of the operation, the blinder can be set to None. +-- +-- If unsure always set a blinder or use signSafer +sign :: Maybe Blinder -- ^ optional blinder + -> HashDescr -- ^ hash descriptor + -> PrivateKey -- ^ private key + -> ByteString -- ^ message to sign + -> Either Error ByteString +sign blinder hashDescr pk m = dp blinder pk `fmap` makeSignature hashDescr (private_size pk) m + +-- | sign message using the private key and by automatically generating a blinder. +signSafer :: MonadRandom m + => HashDescr -- ^ Hash descriptor + -> PrivateKey -- ^ private key + -> ByteString -- ^ message to sign + -> m (Either Error ByteString) +signSafer hashDescr pk m = do + blinder <- generateBlinder (private_n pk) + return (sign (Just blinder) hashDescr pk m) + +-- | verify message with the signed message +verify :: HashDescr -> PublicKey -> ByteString -> ByteString -> Bool +verify hashDescr pk m sm = + case makeSignature hashDescr (public_size pk) m of + Left _ -> False + Right s -> s == (ep pk sm) + +{- makeSignature for sign and verify -} +makeSignature :: HashDescr -> Int -> ByteString -> Either Error ByteString +makeSignature hashDescr klen m = padSignature klen signature + where signature = (digestToASN1 hashDescr) $ (hashFunction hashDescr) m diff --git a/Crypto/PubKey/RSA/PSS.hs b/Crypto/PubKey/RSA/PSS.hs new file mode 100644 index 0000000..7007fba --- /dev/null +++ b/Crypto/PubKey/RSA/PSS.hs @@ -0,0 +1,135 @@ +-- | +-- Module : Crypto.PubKey.RSA.PSS +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +module Crypto.PubKey.RSA.PSS + ( PSSParams(..) + , defaultPSSParams + , defaultPSSParamsSHA1 + -- * Sign and verify functions + , signWithSalt + , sign + , signSafer + , verify + ) where + +import Crypto.Random.Types +import Crypto.PubKey.RSA.Types +import Data.ByteString (ByteString) +import Data.Byteable +import qualified Data.ByteString as B +import Crypto.PubKey.RSA.Prim +import Crypto.PubKey.RSA.Types +import Crypto.PubKey.RSA (generateBlinder) +import Crypto.PubKey.HashDescr +import Crypto.PubKey.MaskGenFunction +import Crypto.Hash +import Data.Bits (xor, shiftR, (.&.)) +import Data.Word + +-- | Parameters for PSS signature/verification. +data PSSParams = PSSParams { pssHash :: HashFunction -- ^ Hash function to use + , pssMaskGenAlg :: MaskGenAlgorithm -- ^ Mask Gen algorithm to use + , pssSaltLength :: Int -- ^ Length of salt. need to be <= to hLen. + , pssTrailerField :: Word8 -- ^ Trailer field, usually 0xbc + } + +-- | Default Params with a specified hash function +defaultPSSParams :: HashFunction -> PSSParams +defaultPSSParams hashF = + PSSParams { pssHash = hashF + , pssMaskGenAlg = mgf1 + , pssSaltLength = B.length $ hashF B.empty + , pssTrailerField = 0xbc + } + +-- | Default Params using SHA1 algorithm. +defaultPSSParamsSHA1 :: PSSParams +defaultPSSParamsSHA1 = defaultPSSParams (toBytes . (hash :: ByteString -> Digest SHA1)) + +-- | Sign using the PSS parameters and the salt explicitely passed as parameters. +-- +-- the function ignore SaltLength from the PSS Parameters +signWithSalt :: ByteString -- ^ Salt to use + -> Maybe Blinder -- ^ optional blinder to use + -> PSSParams -- ^ PSS Parameters to use + -> PrivateKey -- ^ RSA Private Key + -> ByteString -- ^ Message to sign + -> Either Error ByteString +signWithSalt salt blinder params pk m + | k < hashLen + saltLen + 2 = Left InvalidParameters + | otherwise = Right $ dp blinder pk em + where mHash = (pssHash params) m + k = private_size pk + dbLen = k - hashLen - 1 + saltLen = B.length salt + hashLen = B.length (hashF B.empty) + hashF = pssHash params + pubBits = private_size pk * 8 -- to change if public_size is converted in bytes + + m' = B.concat [B.replicate 8 0,mHash,salt] + h = hashF m' + db = B.concat [B.replicate (dbLen - saltLen - 1) 0,B.singleton 1,salt] + dbmask = (pssMaskGenAlg params) hashF h dbLen + maskedDB = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor db dbmask + em = B.concat [maskedDB, h, B.singleton (pssTrailerField params)] + +-- | Sign using the PSS Parameters +sign :: MonadRandom m + => Maybe Blinder -- ^ optional blinder to use + -> PSSParams -- ^ PSS Parameters to use + -> PrivateKey -- ^ RSA Private Key + -> ByteString -- ^ Message to sign + -> m (Either Error ByteString) +sign blinder params pk m = do + salt <- getRandomBytes (pssSaltLength params) + return (signWithSalt salt blinder params pk m) + +-- | Sign using the PSS Parameters and an automatically generated blinder. +signSafer :: MonadRandom m + => PSSParams -- ^ PSS Parameters to use + -> PrivateKey -- ^ private key + -> ByteString -- ^ message to sign + -> m (Either Error ByteString) +signSafer params pk m = do + blinder <- generateBlinder (private_n pk) + sign (Just blinder) params pk m + +-- | Verify a signature using the PSS Parameters +verify :: PSSParams -- ^ PSS Parameters to use to verify, + -- this need to be identical to the parameters when signing + -> PublicKey -- ^ RSA Public Key + -> ByteString -- ^ Message to verify + -> ByteString -- ^ Signature + -> Bool +verify params pk m s + | public_size pk /= B.length s = False + | B.last em /= pssTrailerField params = False + | not (B.all (== 0) ps0) = False + | b1 /= B.singleton 1 = False + | otherwise = h == h' + where -- parameters + hashF = pssHash params + hashLen = B.length (hashF B.empty) + dbLen = public_size pk - hashLen - 1 + pubBits = public_size pk * 8 -- to change if public_size is converted in bytes + -- unmarshall fields + em = ep pk s + maskedDB = B.take (B.length em - hashLen - 1) em + h = B.take hashLen $ B.drop (B.length maskedDB) em + dbmask = (pssMaskGenAlg params) hashF h dbLen + db = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor maskedDB dbmask + (ps0,z) = B.break (== 1) db + (b1,salt) = B.splitAt 1 z + mHash = hashF m + m' = B.concat [B.replicate 8 0,mHash,salt] + h' = hashF m' + +normalizeToKeySize :: Int -> [Word8] -> [Word8] +normalizeToKeySize _ [] = [] -- very unlikely +normalizeToKeySize bits (x:xs) = x .&. mask : xs + where mask = if sh > 0 then 0xff `shiftR` (8-sh) else 0xff + sh = ((bits-1) .&. 0x7) diff --git a/Crypto/PubKey/RSA/Prim.hs b/Crypto/PubKey/RSA/Prim.hs new file mode 100644 index 0000000..26f38c2 --- /dev/null +++ b/Crypto/PubKey/RSA/Prim.hs @@ -0,0 +1,60 @@ +-- | +-- Module : Crypto.PubKey.RSA.Prim +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +module Crypto.PubKey.RSA.Prim + ( + -- * decrypt primitive + dp + -- * encrypt primitive + , ep + ) where + +import Data.ByteString (ByteString) +import Crypto.PubKey.RSA.Types +import Crypto.Number.ModArithmetic (expFast, expSafe) +import Crypto.Number.Serialize (os2ip, i2ospOf_) + +{- dpSlow computes the decrypted message not using any precomputed cache value. + only n and d need to valid. -} +dpSlow :: PrivateKey -> ByteString -> ByteString +dpSlow pk c = i2ospOf_ (private_size pk) $ expSafe (os2ip c) (private_d pk) (private_n pk) + +{- dpFast computes the decrypted message more efficiently if the + precomputed private values are available. mod p and mod q are faster + to compute than mod pq -} +dpFast :: Blinder -> PrivateKey -> ByteString -> ByteString +dpFast (Blinder r rm1) pk c = + i2ospOf_ (private_size pk) (multiplication rm1 (m2 + h * (private_q pk)) (private_n pk)) + where + re = expFast r (public_e $ private_pub pk) (private_n pk) + iC = multiplication re (os2ip c) (private_n pk) + m1 = expSafe iC (private_dP pk) (private_p pk) + m2 = expSafe iC (private_dQ pk) (private_q pk) + h = ((private_qinv pk) * (m1 - m2)) `mod` (private_p pk) + +dpFastNoBlinder :: PrivateKey -> ByteString -> ByteString +dpFastNoBlinder pk c = i2ospOf_ (private_size pk) (m2 + h * (private_q pk)) + where iC = os2ip c + m1 = expSafe iC (private_dP pk) (private_p pk) + m2 = expSafe iC (private_dQ pk) (private_q pk) + h = ((private_qinv pk) * (m1 - m2)) `mod` (private_p pk) + +-- | Compute the RSA decrypt primitive. +-- if the p and q numbers are available, then dpFast is used +-- otherwise, we use dpSlow which only need d and n. +dp :: Maybe Blinder -> PrivateKey -> ByteString -> ByteString +dp blinder pk + | private_p pk /= 0 && private_q pk /= 0 = maybe dpFastNoBlinder dpFast blinder $ pk + | otherwise = dpSlow pk + +-- | Compute the RSA encrypt primitive +ep :: PublicKey -> ByteString -> ByteString +ep pk m = i2ospOf_ (public_size pk) $ expFast (os2ip m) (public_e pk) (public_n pk) + +-- | multiply 2 integers in Zm only performing the modulo operation if necessary +multiplication :: Integer -> Integer -> Integer -> Integer +multiplication a b m = (a * b) `mod` m diff --git a/Crypto/PubKey/RSA/Types.hs b/Crypto/PubKey/RSA/Types.hs new file mode 100644 index 0000000..0af5ca0 --- /dev/null +++ b/Crypto/PubKey/RSA/Types.hs @@ -0,0 +1,86 @@ +-- | +-- Module : Crypto.PubKey.RSA.Types +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good +-- +{-# LANGUAGE DeriveDataTypeable #-} +module Crypto.PubKey.RSA.Types + ( Error(..) + , Blinder(..) + , PublicKey(..) + , PrivateKey(..) + , KeyPair(..) + , private_size + , private_n + , private_e + ) where + +import Data.Data +import Data.Typeable + +-- | Blinder which is used to obfuscate the timing +-- of the decryption primitive (used by decryption and signing). +data Blinder = Blinder !Integer !Integer + deriving (Show,Eq) + +-- | error possible during encryption, decryption or signing. +data Error = + MessageSizeIncorrect -- ^ the message to decrypt is not of the correct size (need to be == private_size) + | MessageTooLong -- ^ the message to encrypt is too long + | MessageNotRecognized -- ^ the message decrypted doesn't have a PKCS15 structure (0 2 .. 0 msg) + | SignatureTooLong -- ^ the message's digest is too long + | InvalidParameters -- ^ some parameters lead to breaking assumptions. + deriving (Show,Eq) + +-- | Represent a RSA public key +data PublicKey = PublicKey + { public_size :: Int -- ^ size of key in bytes + , public_n :: Integer -- ^ public p*q + , public_e :: Integer -- ^ public exponant e + } deriving (Show,Read,Eq,Data,Typeable) + +-- | Represent a RSA private key. +-- +-- Only the pub, d fields are mandatory to fill. +-- +-- p, q, dP, dQ, qinv are by-product during RSA generation, +-- but are useful to record here to speed up massively +-- the decrypt and sign operation. +-- +-- implementations can leave optional fields to 0. +-- +data PrivateKey = PrivateKey + { private_pub :: PublicKey -- ^ public part of a private key (size, n and e) + , private_d :: Integer -- ^ private exponant d + , private_p :: Integer -- ^ p prime number + , private_q :: Integer -- ^ q prime number + , private_dP :: Integer -- ^ d mod (p-1) + , private_dQ :: Integer -- ^ d mod (q-1) + , private_qinv :: Integer -- ^ q^(-1) mod p + } deriving (Show,Read,Eq,Data,Typeable) + +-- | get the size in bytes from a private key +private_size = public_size . private_pub + +-- | get n from a private key +private_n = public_n . private_pub + +-- | get e from a private key +private_e = public_e . private_pub + +-- | Represent RSA KeyPair +-- +-- note the RSA private key contains already an instance of public key for efficiency +newtype KeyPair = KeyPair PrivateKey + deriving (Show,Read,Eq,Data,Typeable) + +-- | Public key of a RSA KeyPair +toPublicKey :: KeyPair -> PublicKey +toPublicKey (KeyPair priv) = private_pub priv + +-- | Private key of a RSA KeyPair +toPrivateKey :: KeyPair -> PrivateKey +toPrivateKey (KeyPair priv) = priv + diff --git a/cryptonite.cabal b/cryptonite.cabal index 02660b8..a223a7b 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -59,6 +59,21 @@ Library Crypto.Hash.Tiger Crypto.Hash.Whirlpool Crypto.PubKey.Curve25519 + Crypto.PubKey.HashDescr + Crypto.PubKey.MaskGenFunction + Crypto.PubKey.DH + Crypto.PubKey.DSA + Crypto.PubKey.ECC.Generate + Crypto.PubKey.ECC.Prim + Crypto.PubKey.ECC.DH + Crypto.PubKey.ECC.ECDSA + Crypto.PubKey.ECC.Types + Crypto.PubKey.RSA + Crypto.PubKey.RSA.PKCS15 + Crypto.PubKey.RSA.Prim + Crypto.PubKey.RSA.PSS + Crypto.PubKey.RSA.OAEP + Crypto.PubKey.RSA.Types Crypto.Random Crypto.Random.Types Crypto.Random.Entropy @@ -86,6 +101,8 @@ Library Crypto.Random.Entropy.Source Crypto.Random.Entropy.Backend Crypto.Random.ChaChaDRG + Crypto.PubKey.Internal + Crypto.PubKey.ElGamal Crypto.Internal.Compat Crypto.Internal.Bytes Crypto.Internal.ByteArray @@ -151,6 +168,7 @@ Test-Suite test-cryptonite , tasty , tasty-quickcheck , tasty-hunit + , tasty-kat , cryptonite ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures default-language: Haskell2010 diff --git a/tests/KAT_PubKey.hs b/tests/KAT_PubKey.hs new file mode 100644 index 0000000..68e3064 --- /dev/null +++ b/tests/KAT_PubKey.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey (tests) where + +import Test.Tasty +import Test.Tasty.HUnit +import Test.Tasty.QuickCheck +import System.IO (hFlush, stdout) + +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as BC + +import Crypto.PubKey.RSA +import Crypto.PubKey.MaskGenFunction +import qualified Crypto.Hash.SHA1 as SHA1 + +import KAT_PubKey.OAEP +import KAT_PubKey.PSS +import KAT_PubKey.DSA +import KAT_PubKey.ECC +import KAT_PubKey.ECDSA + +data VectorMgf = VectorMgf { seed :: ByteString + , dbMask :: ByteString + } + +doMGFTest (i, vmgf) = testCase (show i) (dbMask vmgf @=? actual) + where actual = mgf1 SHA1.hash (seed vmgf) (B.length $ dbMask vmgf) + +vectorsMGF = + [ VectorMgf + { seed = "\xdf\x1a\x89\x6f\x9d\x8b\xc8\x16\xd9\x7c\xd7\xa2\xc4\x3b\xad\x54\x6f\xbe\x8c\xfe" + , dbMask = "\x66\xe4\x67\x2e\x83\x6a\xd1\x21\xba\x24\x4b\xed\x65\x76\xb8\x67\xd9\xa4\x47\xc2\x8a\x6e\x66\xa5\xb8\x7d\xee\x7f\xbc\x7e\x65\xaf\x50\x57\xf8\x6f\xae\x89\x84\xd9\xba\x7f\x96\x9a\xd6\xfe\x02\xa4\xd7\x5f\x74\x45\xfe\xfd\xd8\x5b\x6d\x3a\x47\x7c\x28\xd2\x4b\xa1\xe3\x75\x6f\x79\x2d\xd1\xdc\xe8\xca\x94\x44\x0e\xcb\x52\x79\xec\xd3\x18\x3a\x31\x1f\xc8\x97\x39\xa9\x66\x43\x13\x6e\x8b\x0f\x46\x5e\x87\xa4\x53\x5c\xd4\xc5\x9b\x10\x02\x8d" + } + ] + +tests = testGroup "PubKey" + [ testGroup "MGF1" $ map doMGFTest (zip [0..] vectorsMGF) + , pssTests + , oaepTests + , dsaTests + , eccTests + , ecdsaTests + ] + +--newKats = [ eccKatTests ] diff --git a/tests/KAT_PubKey/DSA.hs b/tests/KAT_PubKey/DSA.hs new file mode 100644 index 0000000..2397067 --- /dev/null +++ b/tests/KAT_PubKey/DSA.hs @@ -0,0 +1,147 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey.DSA (dsaTests) where + +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as BC + +import qualified Crypto.PubKey.DSA as DSA +import qualified Crypto.Hash.SHA1 as SHA1 + +import Test.Tasty +import Test.Tasty.HUnit + +data VectorDSA = VectorDSA + { pgq :: DSA.Params + , msg :: ByteString + , x :: Integer + , y :: Integer + , k :: Integer + , r :: Integer + , s :: Integer + } + +vectorsSHA1 = + [ VectorDSA + { msg = "\x3b\x46\x73\x6d\x55\x9b\xd4\xe0\xc2\xc1\xb2\x55\x3a\x33\xad\x3c\x6c\xf2\x3c\xac\x99\x8d\x3d\x0c\x0e\x8f\xa4\xb1\x9b\xca\x06\xf2\xf3\x86\xdb\x2d\xcf\xf9\xdc\xa4\xf4\x0a\xd8\xf5\x61\xff\xc3\x08\xb4\x6c\x5f\x31\xa7\x73\x5b\x5f\xa7\xe0\xf9\xe6\xcb\x51\x2e\x63\xd7\xee\xa0\x55\x38\xd6\x6a\x75\xcd\x0d\x42\x34\xb5\xcc\xf6\xc1\x71\x5c\xca\xaf\x9c\xdc\x0a\x22\x28\x13\x5f\x71\x6e\xe9\xbd\xee\x7f\xc1\x3e\xc2\x7a\x03\xa6\xd1\x1c\x5c\x5b\x36\x85\xf5\x19\x00\xb1\x33\x71\x53\xbc\x6c\x4e\x8f\x52\x92\x0c\x33\xfa\x37\xf4\xe7" + , x = 0xc53eae6d45323164c7d07af5715703744a63fc3a + , y = 0x313fd9ebca91574e1c2eebe1517c57e0c21b0209872140c5328761bbb2450b33f1b18b409ce9ab7c4cd8fda3391e8e34868357c199e16a6b2eba06d6749def791d79e95d3a4d09b24c392ad89dbf100995ae19c01062056bb14bce005e8731efde175f95b975089bdcdaea562b32786d96f5a31aedf75364008ad4fffebb970b + , k = 0x98cbcc4969d845e2461b5f66383dd503712bbcfa + , r = 0x50ed0e810e3f1c7cb6ac62332058448bd8b284c0 + , s = 0xc6aded17216b46b7e4b6f2a97c1ad7cc3da83fde + , pgq = dsaParams + } + , VectorDSA + { msg = "\xd2\xbc\xb5\x3b\x04\x4b\x3e\x2e\x4b\x61\xba\x2f\x91\xc0\x99\x5f\xb8\x3a\x6a\x97\x52\x5e\x66\x44\x1a\x3b\x48\x9d\x95\x94\x23\x8b\xc7\x40\xbd\xee\xa0\xf7\x18\xa7\x69\xc9\x77\xe2\xde\x00\x38\x77\xb5\xd7\xdc\x25\xb1\x82\xae\x53\x3d\xb3\x3e\x78\xf2\xc3\xff\x06\x45\xf2\x13\x7a\xbc\x13\x7d\x4e\x7d\x93\xcc\xf2\x4f\x60\xb1\x8a\x82\x0b\xc0\x7c\x7b\x4b\x5f\xe0\x8b\x4f\x9e\x7d\x21\xb2\x56\xc1\x8f\x3b\x9d\x49\xac\xc4\xf9\x3e\x2c\xe6\xf3\x75\x4c\x78\x07\x75\x7d\x2e\x11\x76\x04\x26\x12\xcb\x32\xfc\x3f\x4f\x70\x70\x0e\x25" + , x = 0xe65131d73470f6ad2e5878bdc9bef536faf78831 + , y = 0x29bdd759aaa62d4bf16b4861c81cf42eac2e1637b9ecba512bdbc13ac12a80ae8de2526b899ae5e4a231aef884197c944c732693a634d7659abc6975a773f8d3cd5a361fe2492386a3c09aaef12e4a7e73ad7dfc3637f7b093f2c40d6223a195c136adf2ea3fbf8704a675aa7817aa7ec7f9adfb2854d4e05c3ce7f76560313b + , k = 0x87256a64e98cf5be1034ecfa766f9d25d1ac7ceb + , r = 0xa26c00b5750a2d27fe7435b93476b35438b4d8ab + , s = 0x61c9bfcb2938755afa7dad1d1e07c6288617bf70 + , pgq = dsaParams + } + , VectorDSA + { msg = "\xd5\x43\x1e\x6b\x16\xfd\xae\x31\x48\x17\x42\xbd\x39\x47\x58\xbe\xb8\xe2\x4f\x31\x94\x7e\x19\xb7\xea\x7b\x45\x85\x21\x88\x22\x70\xc1\xf4\x31\x92\xaa\x05\x0f\x44\x85\x14\x5a\xf8\xf3\xf9\xc5\x14\x2d\x68\xb8\x50\x18\xd2\xec\x9c\xb7\xa3\x7b\xa1\x2e\xd2\x3e\x73\xb9\x5f\xd6\x80\xfb\xa3\xc6\x12\x65\xe9\xf5\xa0\xa0\x27\xd7\x0f\xad\x0c\x8a\xa0\x8a\x3c\xbf\xbe\x99\x01\x8d\x00\x45\x38\x61\x73\xe5\xfa\xe2\x25\xfa\xeb\xe0\xce\xf5\xdd\x45\x91\x0f\x40\x0a\x86\xc2\xbe\x4e\x15\x25\x2a\x16\xde\x41\x20\xa2\x67\xbe\x2b\x59\x4d" + , x = 0x20bcabc6d9347a6e79b8e498c60c44a19c73258c + , y = 0x23b4f404aa3c575e550bb320fdb1a085cd396a10e5ebc6771da62f037cab19eacd67d8222b6344038c4f7af45f5e62b55480cbe2111154ca9697ca76d87b56944138084e74c6f90a05cf43660dff8b8b3fabfcab3f0e4416775fdf40055864be102b4587392e77752ed2aeb182ee4f70be4a291dbe77b84a44ee34007957b1e0 + , k = 0x7d9bcfc9225432de9860f605a38d389e291ca750 + , r = 0x3f0a4ad32f0816821b8affb518e9b599f35d57c2 + , s = 0xea06638f2b2fc9d1dfe99c2a492806b497e2b0ea + , pgq = dsaParams + } + , VectorDSA + { msg = "\x85\x66\x2b\x69\x75\x50\xe4\x91\x5c\x29\xe3\x38\xb6\x24\xb9\x12\x84\x5d\x6d\x1a\x92\x0d\x9e\x4c\x16\x04\xdd\x47\xd6\x92\xbc\x7c\x0f\xfb\x95\xae\x61\x4e\x85\x2b\xeb\xaf\x15\x73\x75\x8a\xd0\x1c\x71\x3c\xac\x0b\x47\x6e\x2f\x12\x17\x45\xa3\xcf\xee\xff\xb2\x44\x1f\xf6\xab\xfb\x9b\xbe\xb9\x8a\xa6\x34\xca\x6f\xf5\x41\x94\x7d\xcc\x99\x27\x65\x9d\x44\xf9\x5c\x5f\xf9\x17\x0f\xdc\x3c\x86\x47\x3c\xb6\x01\xba\x31\xb4\x87\xfe\x59\x36\xba\xc5\xd9\xc6\x32\xcb\xcc\x3d\xb0\x62\x46\xba\x01\xc5\x5a\x03\x8d\x79\x7f\xe3\xf6\xc3" + , x = 0x52d1fbe687aa0702a51a5bf9566bd51bd569424c + , y = 0x6bc36cb3fa61cecc157be08639a7ca9e3de073b8a0ff23574ce5ab0a867dfd60669a56e60d1c989b3af8c8a43f5695d503e3098963990e12b63566784171058eace85c728cd4c08224c7a6efea75dca20df461013c75f40acbc23799ebee7f3361336dadc4a56f305708667bfe602b8ea75a491a5cf0c06ebd6fdc7161e10497 + , k = 0x960c211891c090d05454646ebac1bfe1f381e82b + , r = 0x3bc29dee96957050ba438d1b3e17b02c1725d229 + , s = 0x0af879cf846c434e08fb6c63782f4d03e0d88865 + , pgq = dsaParams + } + , VectorDSA + { msg = "\x87\xb6\xe7\x5b\x9f\x8e\x99\xc4\xdd\x62\xad\xb6\x93\xdd\x58\x90\xed\xff\x1b\xd0\x02\x8f\x4e\xf8\x49\xdf\x0f\x1d\x2c\xe6\xb1\x81\xfc\x3a\x55\xae\xa6\xd0\xa1\xf0\xae\xca\xb8\xed\x9e\x24\x8a\x00\xe9\x6b\xe7\x94\xa7\xcf\xba\x12\x46\xef\xb7\x10\xef\x4b\x37\x47\x1c\xef\x0a\x1b\xcf\x55\xce\xbc\x8d\x5a\xd0\x71\x61\x2b\xd2\x37\xef\xed\xd5\x10\x23\x62\xdb\x07\xa1\xe2\xc7\xa6\xf1\x5e\x09\xfe\x64\xba\x42\xb6\x0a\x26\x28\xd8\x69\xae\x05\xef\x61\x1f\xe3\x8d\x9c\xe1\x5e\xee\xc9\xbb\x3d\xec\xc8\xdc\x17\x80\x9f\x3b\x6e\x95" + , x = 0xc86a54ec5c4ec63d7332cf43ddb082a34ed6d5f5 + , y = 0x014ac746d3605efcb8a2c7dae1f54682a262e27662b252c09478ce87d0aaa522d7c200043406016c0c42896d21750b15dbd57f9707ec37dcea5651781b67ad8d01f5099fe7584b353b641bb159cc717d8ceb18b66705e656f336f1214b34f0357e577ab83641969e311bf40bdcb3ffd5e0bb59419f229508d2f432cc2859ff75 + , k = 0x6c445cee68042553fbe63be61be4ddb99d8134af + , r = 0x637e07a5770f3dc65e4506c68c770e5ef6b8ced3 + , s = 0x7dfc6f83e24f09745e01d3f7ae0ed1474e811d47 + , pgq = dsaParams + } + , VectorDSA + { msg = "\x22\x59\xee\xad\x2d\x6b\xbc\x76\xd4\x92\x13\xea\x0d\xc8\xb7\x35\x0a\x97\x69\x9f\x22\x34\x10\x44\xc3\x94\x07\x82\x36\x4a\xc9\xea\x68\x31\x79\xa4\x38\xa5\xea\x45\x99\x8d\xf9\x7c\x29\x72\xda\xe0\x38\x51\xf5\xbe\x23\xfa\x9f\x04\x18\x2e\x79\xdd\xb2\xb5\x6d\xc8\x65\x23\x93\xec\xb2\x7f\x3f\x3b\x7c\x8a\x8d\x76\x1a\x86\xb3\xb8\xf4\xd4\x1a\x07\xb4\xbe\x7d\x02\xfd\xde\xfc\x42\xb9\x28\x12\x4a\x5a\x45\xb9\xf4\x60\x90\x42\x20\x9b\x3a\x7f\x58\x5b\xd5\x14\xcc\x39\xc0\x0e\xff\xcc\x42\xc7\xfe\x70\xfa\x83\xed\xf8\xa3\x2b\xf4" + , x = 0xaee6f213b9903c8069387e64729a08999e5baf65 + , y = 0x0fe74045d7b0d472411202831d4932396f242a9765e92be387fd81bbe38d845054528b348c03984179b8e505674cb79d88cc0d8d3e8d7392f9aa773b29c29e54a9e326406075d755c291fcedbcc577934c824af988250f64ed5685fce726cff65e92d708ae11cbfaa958ab8d8b15340a29a137b5b4357f7ed1c7a5190cbf98a4 + , k = 0xe1704bae025942e2e63c6d76bab88da79640073a + , r = 0x83366ba3fed93dfb38d541203ecbf81c363998e2 + , s = 0x1fe299c36a1332f23bf2e10a6c6a4e0d3cdd2bf4 + , pgq = dsaParams + } + , VectorDSA + { msg = "\x21\x9e\x8d\xf5\xbf\x88\x15\x90\x43\x0e\xce\x60\x82\x50\xf7\x67\x0d\xc5\x65\x37\x24\x93\x02\x42\x9e\x28\xec\xfe\xb9\xce\xaa\xa5\x49\x10\xa6\x94\x90\xf7\x65\xf3\xdf\x82\xe8\xb0\x1c\xd7\xd7\x6e\x56\x1d\x0f\x6c\xe2\x26\xef\x3c\xf7\x52\xca\xda\x6f\xeb\xdc\x5b\xf0\x0d\x67\x94\x7f\x92\xd4\x20\x51\x6b\x9e\x37\xc9\x6c\x8f\x1f\x2d\xa0\xb0\x75\x09\x7c\x3b\xda\x75\x8a\x8d\x91\xbd\x2e\xbe\x9c\x75\xcf\x14\x7f\x25\x4c\x25\x69\x63\xb3\x3b\x67\xd0\x2b\x6a\xa0\x9e\x7d\x74\x65\xd0\x38\xe5\x01\x95\xec\xe4\x18\x9b\x41\xe7\x68" + , x = 0x699f1c07aa458c6786e770b40197235fe49cf21a + , y = 0x3a41b0678ff3c4dde20fa39772bac31a2f18bae4bedec9e12ee8e02e30e556b1a136013bef96b0d30b568233dcecc71e485ed75c922afb4d0654e709bee84993792130220e3005fdb06ebdfc0e2df163b5ec424e836465acd6d92e243c86f2b94b26b8d73bd9cf722c757e0b80b0af16f185de70e8ca850b1402d126ea60f309 + , k = 0x5bbb795bfa5fa72191fed3434a08741410367491 + , r = 0x579761039ae0ddb81106bf4968e320083bbcb947 + , s = 0x503ea15dbac9dedeba917fa8e9f386b93aa30353 + , pgq = dsaParams + } + , VectorDSA + { msg = "\x2d\xa7\x9d\x06\x78\x85\xeb\x3c\xcf\x5e\x29\x3a\xe3\xb1\xd8\x22\x53\x22\x20\x3a\xbb\x5a\xdf\xde\x3b\x0f\x53\xbb\xe2\x4c\x4f\xe0\x01\x54\x1e\x11\x83\xd8\x70\xa9\x97\xf1\xf9\x46\x01\x00\xb5\xd7\x11\x92\x31\x80\x15\x43\x45\x28\x7a\x02\x14\xcf\x1c\xac\x37\xb7\xa4\x7d\xfb\xb2\xa0\xe8\xce\x49\x16\xf9\x4e\xbd\x6f\xa5\x4e\x31\x5b\x7a\x8e\xb5\xb6\x3c\xd9\x54\xc5\xba\x05\xc1\xbf\x7e\x33\xa4\xe8\xa1\x51\xf3\x2d\x28\x77\xb0\x17\x29\xc1\xad\x0e\x7c\x01\xbb\x8a\xe7\x23\xc9\x95\x18\x38\x03\xe4\x56\x36\x52\x0e\xa3\x8c\xa1" + , x = 0xd6e08c20c82949ddba93ea81eb2fea8c595894dc + , y = 0x56f7272210f316c51af8bfc45a421fd4e9b1043853271b7e79f40936f0adcf262a86097aa86e19e6cb5307685d863dba761342db6c973b3849b1e060aca926f41fe07323601062515ae85f3172b8f34899c621d59fa21f73d5ae97a3deb5e840b25a18fd580862fd7b1cf416c7ae9fc5842a0197fdb0c5173ff4a4f102a8cf89 + , k = 0x6d72c30d4430959800740f2770651095d0c181c2 + , r = 0x5dd90d69add67a5fae138eec1aaff0229aa4afc4 + , s = 0x47f39c4db2387f10762f45b80dfd027906d7ef04 + , pgq = dsaParams + } + , VectorDSA + { msg = "\xba\x30\xd8\x5b\xe3\x57\xe7\xfb\x29\xf8\xa0\x7e\x1f\x12\x7b\xaa\xa2\x4b\x2e\xe0\x27\xf6\x4c\xb5\xef\xee\xc6\xaa\xea\xbc\xc7\x34\x5c\x5d\x55\x6e\xbf\x4b\xdc\x7a\x61\xc7\x7c\x7b\x7e\xa4\x3c\x73\xba\xbc\x18\xf7\xb4\x80\x77\x22\xda\x23\x9e\x45\xdd\xf2\x49\x84\x9c\xbb\xfe\x35\x07\x11\x2e\xbf\x87\xd7\xef\x56\x0c\x2e\x7d\x39\x1e\xd8\x42\x4f\x87\x10\xce\xa4\x16\x85\x14\x3e\x30\x06\xf8\x1b\x68\xfb\xb4\xd5\xf9\x64\x4c\x7c\xd1\x0f\x70\x92\xef\x24\x39\xb8\xd1\x8c\x0d\xf6\x55\xe0\x02\x89\x37\x2a\x41\x66\x38\x5d\x64\x0c" + , x = 0x50018482864c1864e9db1f04bde8dbfd3875c76d + , y = 0x0942a5b7a72ab116ead29308cf658dfe3d55d5d61afed9e3836e64237f9d6884fdd827d2d5890c9a41ae88e7a69fc9f345ade9c480c6f08cff067c183214c227236cedb6dd1283ca2a602574e8327510221d4c27b162143b7002d8c726916826265937b87be9d5ec6d7bd28fb015f84e0ab730da7a4eaf4ef3174bf0a22a6392 + , k = 0xdf3a9348f37b5d2d4c9176db266ae388f1fa7e0f + , r = 0x448434b214eee38bde080f8ec433e8d19b3ddf0d + , s = 0x0c02e881b777923fe0ea674f2621298e00199d5f + , pgq = dsaParams + } + , VectorDSA + { msg = "\x83\x49\x9e\xfb\x06\xbb\x7f\xf0\x2f\xfb\x46\xc2\x78\xa5\xe9\x26\x30\xac\x5b\xc3\xf9\xe5\x3d\xd2\xe7\x8f\xf1\x5e\x36\x8c\x7e\x31\xaa\xd7\x7c\xf7\x71\xf3\x5f\xa0\x2d\x0b\x5f\x13\x52\x08\xa4\xaf\xdd\x86\x7b\xb2\xec\x26\xea\x2e\x7d\xd6\x4c\xde\xf2\x37\x50\x8a\x38\xb2\x7f\x39\xd8\xb2\x2d\x45\xca\xc5\xa6\x8a\x90\xb6\xea\x76\x05\x86\x45\xf6\x35\x6a\x93\x44\xd3\x6f\x00\xec\x66\x52\xea\xa4\xe9\xba\xe7\xb6\x94\xf9\xf1\xfc\x8c\x6c\x5e\x86\xfa\xdc\x7b\x27\xa2\x19\xb5\xc1\xb2\xae\x80\xa7\x25\xe5\xf6\x11\x65\xfe\x2e\xdc" + , x = 0xae56f66b0a9405b9cca54c60ec4a3bb5f8be7c3f + , y = 0xa01542c3da410dd57930ca724f0f507c4df43d553c7f69459939685941ceb95c7dcc3f175a403b359621c0d4328e98f15f330a63865baf3e7eb1604a0715e16eed64fd14b35d3a534259a6a7ddf888c4dbb5f51bbc6ed339e5bb2a239d5cfe2100ac8e2f9c16e536f25119ab435843af27dc33414a9e4602f96d7c94d6021cec + , k = 0x8857ff301ad0169d164fa269977a116e070bac17 + , r = 0x8c2fab489c34672140415d41a65cef1e70192e23 + , s = 0x3df86a9e2efe944a1c7ea9c30cac331d00599a0e + , pgq = dsaParams + } + ] + where -- (p,g,q) + dsaParams = DSA.Params + { DSA.params_p = 0xa8f9cd201e5e35d892f85f80e4db2599a5676a3b1d4f190330ed3256b26d0e80a0e49a8fffaaad2a24f472d2573241d4d6d6c7480c80b4c67bb4479c15ada7ea8424d2502fa01472e760241713dab025ae1b02e1703a1435f62ddf4ee4c1b664066eb22f2e3bf28bb70a2a76e4fd5ebe2d1229681b5b06439ac9c7e9d8bde283 + , DSA.params_g = 0x2b3152ff6c62f14622b8f48e59f8af46883b38e79b8c74deeae9df131f8b856e3ad6c8455dab87cc0da8ac973417ce4f7878557d6cdf40b35b4a0ca3eb310c6a95d68ce284ad4e25ea28591611ee08b8444bd64b25f3f7c572410ddfb39cc728b9c936f85f419129869929cdb909a6a3a99bbe089216368171bd0ba81de4fe33 + , DSA.params_q = 0xf85f0f83ac4df7ea0cdf8f469bfeeaea14156495 + } + +vectorToPrivate :: VectorDSA -> DSA.PrivateKey +vectorToPrivate vector = DSA.PrivateKey + { DSA.private_x = x vector + , DSA.private_params = pgq vector + } + +vectorToPublic :: VectorDSA -> DSA.PublicKey +vectorToPublic vector = DSA.PublicKey + { DSA.public_y = y vector + , DSA.public_params = pgq vector + } + +doSignatureTest (i, vector) = testCase (show i) (expected @=? actual) + where expected = Just $ DSA.Signature (r vector) (s vector) + actual = DSA.signWith (k vector) (vectorToPrivate vector) SHA1.hash (msg vector) + +doVerifyTest (i, vector) = testCase (show i) (True @=? actual) + where actual = DSA.verify SHA1.hash (vectorToPublic vector) (DSA.Signature (r vector) (s vector)) (msg vector) + +dsaTests = testGroup "DSA" + [ testGroup "SHA1" + [ testGroup "signature" $ map doSignatureTest (zip [0..] vectorsSHA1) + , testGroup "verify" $ map doVerifyTest (zip [0..] vectorsSHA1) + ] + ] diff --git a/tests/KAT_PubKey/ECC.hs b/tests/KAT_PubKey/ECC.hs new file mode 100644 index 0000000..aa3a150 --- /dev/null +++ b/tests/KAT_PubKey/ECC.hs @@ -0,0 +1,145 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey.ECC (eccTests, eccKatTests) where + +import Control.Arrow (second) +import Control.Applicative +import Data.ByteString (ByteString) +import Crypto.Number.Serialize + +import qualified Crypto.PubKey.ECC.Types as ECC +import qualified Crypto.PubKey.ECC.Prim as ECC + +import Test.Tasty.KAT +import Test.Tasty.KAT.FileLoader + +import Test.Tasty +import Test.Tasty.HUnit + + +data VectorPoint = VectorPoint + { curve :: ECC.Curve + , x :: Integer + , y :: Integer + , valid :: Bool + } + +vectorsPoint = + [ VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x491c0c4761b0a4a147b5e4ce03a531546644f5d1e3d05e57 + , y = 0x6fa5addd47c5d6be3933fbff88f57a6c8ca0232c471965de + , valid = False -- point not on curve + } + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x646c22e8aa5f7833390e0399155ac198ae42470bba4fc834 + , y = 0x8d4afcfffd80e69a4d180178b37c44572495b7b267ee32a9 + , valid = True + } + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x4c6b9ea0dec92ecfff7799470be6a2277b9169daf45d54bb + , y = 0xf0eab42826704f51b26ae98036e83230becb639dd1964627 + , valid = False -- point not on curve + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x0673c8bb717b055c3d6f55c06acfcfb7260361ed3ec0f414 + , y = 0xba8b172826eb0b854026968d2338a180450a27906f6eddea + , valid = True + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x82c949295156192df0b52480e38c810751ac570daec460a3 + , y = 0x200057ada615c80b8ff256ce8d47f2562b74a438f1921ac3 + , valid = False -- point not on curve + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x284fbaa76ce0faae2ca4867d01092fa1ace5724cd12c8dd0 + , y = 0xe42af3dbf3206be3fcbcc3a7ccaf60c73dc29e7bb9b44fca + , valid = True + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x1b574acd4fb0f60dde3e3b5f3f0e94211f95112e43cba6fd2 + , y = 0xbcc1b8a770f01a22e84d7f14e44932ffe094d8e3b1e6ac26 + , valid = False -- x or y out of range + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x16ba109f1f1bb44e0d05b80181c03412ea764a59601d17e9f + , y = 0x0569a843dbb4e287db420d6b9fe30cd7b5d578b052315f56 + , valid = False -- x or y out of range + } + + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x1333308a7c833ede5189d25ea3525919c9bd16370d904938d + , y = 0xb10fd01d67df75ff9b726c700c1b50596c9f0766ea56f80e + , valid = False -- x or y out of range + } + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x9671ec444cff24c8a5be80b018fa505ed6109a731e88c91a + , y = 0xfe79dae23008e46bf4230c895aab261a95845a77f06d0655 + , valid = True + } + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0x158e8b6f0b14216bc52fe8897b4305d870ede70436a96741d + , y = 0xfb3f970b19a313571a1a23be310923f85acc1cab0a157cbd + , valid = False -- x or y out of range + } + , VectorPoint + { curve = ECC.getCurveByName ECC.SEC_p192r1 + , x = 0xace95b650c08f73dbb4fa7b4bbdebd6b809a25b28ed135ef + , y = 0xe9b8679404166d1329dd539ad52aad9a1b6681f5f26bb9aa + , valid = False -- point not on curve + } + ] + +doPointValidTest (i, vector) = testCase (show i) (valid vector @=? ECC.isPointValid (curve vector) (ECC.Point (x vector) (y vector))) + +eccTests = testGroup "ECC" + [ testGroup "valid-point" $ map doPointValidTest (zip [0..] vectorsPoint) + ] + +eccKatTests = do + res <- testKatLoad "KATs/ECC-PKV.txt" (map (second (map toVector)) . katLoaderSimple) + return $ testKatDetailed {-Grouped-} "ECC/valid-point" res (\g vect -> do + let c = ECC.getCurveByName <$> case g of + "P-192" -> Just ECC.SEC_p192r1 + "P-224" -> Just ECC.SEC_p224r1 + "P-256" -> Just ECC.SEC_p256r1 + "P-384" -> Just ECC.SEC_p384r1 + "P-521" -> Just ECC.SEC_p521r1 + "B-163" -> Just ECC.SEC_t163r2 + "B-233" -> Just ECC.SEC_t233r1 + "B-283" -> Just ECC.SEC_t283r1 + "B-409" -> Just ECC.SEC_t409r1 + "B-571" -> Just ECC.SEC_t571r1 + "" -> Nothing + _ -> Nothing +{- + "K-163" -> Just ECC.SEC_t163k1 + "K-233" -> Just ECC.SEC_t233k1 + "K-283" -> Just ECC.SEC_t283k1 + "K-409" -> Just ECC.SEC_t409k1 + "K-571" -> Just ECC.SEC_t571k1 +-} + case c of + Nothing -> return True + Just curve -> do + return (ECC.isPointValid curve (ECC.Point (x vect) (y vect)) == valid vect) + ) + + where toVector kvs = + case sequence $ map (flip lookup kvs) [ "Qx", "Qy", "Result" ] of + Just [qx,qy,res] -> VectorPoint undefined (valueHexInteger qx) (valueHexInteger qy) (head res /= 'F') + Nothing -> error ("ERROR CRAP: " ++ show kvs) -- VectorPoint undefined 0 0 True diff --git a/tests/KAT_PubKey/ECDSA.hs b/tests/KAT_PubKey/ECDSA.hs new file mode 100644 index 0000000..b861222 --- /dev/null +++ b/tests/KAT_PubKey/ECDSA.hs @@ -0,0 +1,95 @@ +-- Test vectors are taken from GEC2: www.secg.org/collateral/gec2.pdf +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey.ECDSA (ecdsaTests) where + +import Data.ByteString (ByteString) + +import Crypto.Number.Serialize + +import qualified Crypto.PubKey.ECC.ECDSA as ECDSA +import qualified Crypto.PubKey.ECC.Types as ECC +import qualified Crypto.Hash.SHA1 as SHA1 + +import Test.Tasty.HUnit +import Test.Tasty + +data VectorECDSA = VectorECDSA + { curve :: ECC.Curve + , msg :: ByteString + , d :: Integer + , q :: ECC.Point + , k :: Integer + , r :: Integer + , s :: Integer + } + +vectorsSHA1 = + [ VectorECDSA + { curve = ECC.getCurveByName ECC.SEC_p160r1 + , msg = "abc" + , d = 971761939728640320549601132085879836204587084162 + , q = ECC.Point 466448783855397898016055842232266600516272889280 + 1110706324081757720403272427311003102474457754220 + , k = 702232148019446860144825009548118511996283736794 + , r = 1176954224688105769566774212902092897866168635793 + , s = 299742580584132926933316745664091704165278518100 + } + -- from official ECDSA KATs + , VectorECDSA + { curve = ECC.getCurveByName ECC.SEC_t163k1 + , msg = i2osp 0xa2c1a03fdd00521bb08fc88d20344321977aaf637ef9d5470dd7d2c8628fc8d0d1f1d3587c6b3fd02386f8c13db341b14748a9475cc63baf065df64054b27d5c2cdf0f98e3bbb81d0b5dc94f8cdb87acf75720f6163de394c8c6af360bc1acb85b923a493b7b27cc111a257e36337bd94eb0fab9d5e633befb1ae7f1b244bfaa + , d = 0x00000011f2626d90d26cb4c0379043b26e64107fc + , q = ECC.Point 0x0389fa5ad7f8304325a8c060ef7dcb83042c045bc + 0x0eefa094a5054da196943cc80509dcb9f59e5bc2e + , k = 0x0000000c3a4ff97286126dab1e5089395fcc47ebb + , r = 0x0dbe6c3a1dc851e7f2338b5c26c62b4b37bf8035c + , s = 0x1c76458135b1ff9fbd23009b8414a47996126b56a + } + , VectorECDSA + { curve = ECC.getCurveByName ECC.SEC_t163k1 + , msg = i2osp 0x67048080daaeb77d3ac31babdf8be23dbe75ceb4dfb94aa8113db5c5dcb6fe14b70f717b7b0ed0881835a66a86e6d840ffcb7d976c75ef2d1d4322fbbc86357384e24707aef88cea2c41a01a9a3d1b9e72ce650c7fdecc4f9448d3a77df6cdf13647ab295bb3132de0b1b2c402d8d2de7d452f1e003e0695de1470d1064eee16 + , d = 0x00000006a3803301daee9af09bb5b6c991a4f49a4 + , q = ECC.Point 0x4b500f555e857da8c299780130c5c3f48f02ee322 0x5c1c0ae25b47f06cc46fb86b12d2d8c0ba6a4bf07 + , k = 0x0000002f39fbf77f3e0dc046116de692b6cf91b16 + , r = 0x3d3eeda42f65d727f4a564f1415654356c6c57a6c + , s = 0x35e4d43c5f08baddf138449db1ad0b7872552b7cd + } + , VectorECDSA + { curve = ECC.getCurveByName ECC.SEC_t163k1 + , msg = i2osp 0x77e007dc2acd7248256165a4b30e98986f51a81efd926b85f74c81bc2a6d2bcd030060a844091e22fbb0ff3db5a20caaefb5d58ccdcbc27f0ff8a4d940e78f303079ec1ca5b0ca3d4ecc7580f8b34a9f0496c9e719d2ec3e1614b7644bc11179e895d2c0b58a1da204fbf0f6e509f97f983eacb6487092caf6e8e4e6b3c458b2 + , d = 0x0000002e28676514bd93fea11b62db0f6e324b18d + , q = ECC.Point 0x3f9c90b71f6a1de20a2716f38ef1b5f98c757bd42 0x2ff0a5d266d447ef62d43fbca6c34c08c1ce35a40 + , k = 0x00000001233ae699883e74e7f4dfb5279ff22280a + , r = 0x39de3cd2cf04145e522b8fba3f23e9218226e0860 + , s = 0x2af62bfb3cfa202e2342606ee5bb0934c3b0375b6 + } + , VectorECDSA + { curve = ECC.getCurveByName ECC.SEC_t163k1 + , msg = i2osp 0xfbacfcce4688748406ddf5c3495021eef8fb399865b649eb2395a04a1ab28335da2c236d306fcc59f7b65ea931cf0139571e1538ede5688958c3ac69f47a285362f5ad201f89cc735b7b465408c2c41b310fc8908d0be45054df2a7351fae36b390e842f3b5cdd9ad832940df5b2d25c2ed43ce86eaf2508bcf401ae58bb1d47 + , d = 0x000000361dd088e3a6d3c910686c8dce57e5d4d8e + , q = ECC.Point 0x064f905c1da9d7e9c32d81890ae6f30dcc7839d32 0x06f1faedb6d9032016d3b681e7cf69c29d29eb27b + , k = 0x00000022f723e9f5da56d3d0837d5dca2f937395f + , r = 0x374cdc8571083fecfbd4e25e1cd69ecc66b715f2d + , s = 0x313b10949222929b2f20b15d446c27d6dcae3f086 + } + ] + +vectorToPrivate :: VectorECDSA -> ECDSA.PrivateKey +vectorToPrivate vector = ECDSA.PrivateKey (curve vector) (d vector) + +vectorToPublic :: VectorECDSA -> ECDSA.PublicKey +vectorToPublic vector = ECDSA.PublicKey (curve vector) (q vector) + +doSignatureTest (i, vector) = testCase (show i) (expected @=? actual) + where expected = Just $ ECDSA.Signature (r vector) (s vector) + actual = ECDSA.signWith (k vector) (vectorToPrivate vector) SHA1.hash (msg vector) + +doVerifyTest (i, vector) = testCase (show i) (True @=? actual) + where actual = ECDSA.verify SHA1.hash (vectorToPublic vector) (ECDSA.Signature (r vector) (s vector)) (msg vector) + +ecdsaTests = testGroup "ECDSA" + [ testGroup "SHA1" + [ testGroup "signature" $ map doSignatureTest (zip [0..] vectorsSHA1) + , testGroup "verify" $ map doVerifyTest (zip [0..] vectorsSHA1) + ] + ] diff --git a/tests/KAT_PubKey/OAEP.hs b/tests/KAT_PubKey/OAEP.hs new file mode 100644 index 0000000..89ed9af --- /dev/null +++ b/tests/KAT_PubKey/OAEP.hs @@ -0,0 +1,104 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey.OAEP (oaepTests) where + +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as BC + +import Crypto.PubKey.RSA +import Crypto.PubKey.MaskGenFunction +import qualified Crypto.PubKey.RSA.OAEP as OAEP +import qualified Crypto.Hash.SHA1 as SHA1 + +import Test.Tasty +import Test.Tasty.HUnit + +rsaKeyInt = PrivateKey + { private_pub = PublicKey + { public_n = 0xbbf82f090682ce9c2338ac2b9da871f7368d07eed41043a440d6b6f07454f51fb8dfbaaf035c02ab61ea48ceeb6fcd4876ed520d60e1ec4619719d8a5b8b807fafb8e0a3dfc737723ee6b4b7d93a2584ee6a649d060953748834b2454598394ee0aab12d7b61a51f527a9a41f6c1687fe2537298ca2a8f5946f8e5fd091dbdcb + , public_e = 0x11 + , public_size = 128 + } + , private_d = 0xa5dafc5341faf289c4b988db30c1cdf83f31251e0668b42784813801579641b29410b3c7998d6bc465745e5c392669d6870da2c082a939e37fdcb82ec93edac97ff3ad5950accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b8883fe4463a4bc85b1cb3c1 + , private_p = 0xeecfae81b1b9b3c908810b10a1b5600199eb9f44aef4fda493b81a9e3d84f632124ef0236e5d1e3b7e28fae7aa040a2d5b252176459d1f397541ba2a58fb6599 + , private_q = 0xc97fb1f027f453f6341233eaaad1d9353f6c42d08866b1d05a0f2035028b9d869840b41666b42e92ea0da3b43204b5cfce3352524d0416a5a441e700af461503 + , private_dP = 0x54494ca63eba0337e4e24023fcd69a5aeb07dddc0183a4d0ac9b54b051f2b13ed9490975eab77414ff59c1f7692e9a2e202b38fc910a474174adc93c1f67c981 + , private_dQ = 0x471e0290ff0af0750351b7f878864ca961adbd3a8a7e991c5c0556a94c3146a7f9803f8f6f8ae342e931fd8ae47a220d1b99a495849807fe39f9245a9836da3d + , private_qinv = 0xb06c4fdabb6301198d265bdbae9423b380f271f73453885093077fcd39e2119fc98632154f5883b167a967bf402b4e9e2e0f9656e698ea3666edfb25798039f7 + } + +rsaKey1 = PrivateKey + { private_pub = PublicKey + { public_n = 0xa8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0cbc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bdbf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb + , public_e = 0x010001 + , public_size = 128 + } + , private_d = 0x53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf119517ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d65a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1 + , private_p = 0xd32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c2f26a471dcad212eac7ca39d + , private_q = 0xcc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb33d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af72bfe9a030e860b0288b5d77 + , private_dP = 0x0e12bf1718e9cef5599ba1c3882fe8046a90874eefce8f2ccc20e4f2741fb0a33a3848aec9c9305fbecbd2d76819967d4671acc6431e4037968db37878e695c1 + , private_dQ = 0x95297b0f95a2fa67d00707d609dfd4fc05c89dafc2ef6d6ea55bec771ea333734d9251e79082ecda866efef13c459e1a631386b7e354c899f5f112ca85d71583 + , private_qinv = 0x4f456c502493bdc0ed2ab756a3a6ed4d67352a697d4216e93212b127a63d5411ce6fa98d5dbefd73263e3728142743818166ed7dd63687dd2a8ca1d2f4fbd8e1 + } + + +data VectorOAEP = VectorOAEP { seed :: ByteString + , message :: ByteString + , cipherText :: ByteString + } +vectorInt = VectorOAEP + { message = "\xd4\x36\xe9\x95\x69\xfd\x32\xa7\xc8\xa0\x5b\xbc\x90\xd3\x2c\x49" + , seed = "\xaa\xfd\x12\xf6\x59\xca\xe6\x34\x89\xb4\x79\xe5\x07\x6d\xde\xc2\xf0\x6c\xb5\x8f" + , cipherText = "\x12\x53\xe0\x4d\xc0\xa5\x39\x7b\xb4\x4a\x7a\xb8\x7e\x9b\xf2\xa0\x39\xa3\x3d\x1e\x99\x6f\xc8\x2a\x94\xcc\xd3\x00\x74\xc9\x5d\xf7\x63\x72\x20\x17\x06\x9e\x52\x68\xda\x5d\x1c\x0b\x4f\x87\x2c\xf6\x53\xc1\x1d\xf8\x23\x14\xa6\x79\x68\xdf\xea\xe2\x8d\xef\x04\xbb\x6d\x84\xb1\xc3\x1d\x65\x4a\x19\x70\xe5\x78\x3b\xd6\xeb\x96\xa0\x24\xc2\xca\x2f\x4a\x90\xfe\x9f\x2e\xf5\xc9\xc1\x40\xe5\xbb\x48\xda\x95\x36\xad\x87\x00\xc8\x4f\xc9\x13\x0a\xde\xa7\x4e\x55\x8d\x51\xa7\x4d\xdf\x85\xd8\xb5\x0d\xe9\x68\x38\xd6\x06\x3e\x09\x55" + } + +vectorsKey1 = + [ VectorOAEP -- 1.1 + { message = "\x66\x28\x19\x4e\x12\x07\x3d\xb0\x3b\xa9\x4c\xda\x9e\xf9\x53\x23\x97\xd5\x0d\xba\x79\xb9\x87\x00\x4a\xfe\xfe\x34" + , seed = "\x18\xb7\x76\xea\x21\x06\x9d\x69\x77\x6a\x33\xe9\x6b\xad\x48\xe1\xdd\xa0\xa5\xef" + , cipherText = "\x35\x4f\xe6\x7b\x4a\x12\x6d\x5d\x35\xfe\x36\xc7\x77\x79\x1a\x3f\x7b\xa1\x3d\xef\x48\x4e\x2d\x39\x08\xaf\xf7\x22\xfa\xd4\x68\xfb\x21\x69\x6d\xe9\x5d\x0b\xe9\x11\xc2\xd3\x17\x4f\x8a\xfc\xc2\x01\x03\x5f\x7b\x6d\x8e\x69\x40\x2d\xe5\x45\x16\x18\xc2\x1a\x53\x5f\xa9\xd7\xbf\xc5\xb8\xdd\x9f\xc2\x43\xf8\xcf\x92\x7d\xb3\x13\x22\xd6\xe8\x81\xea\xa9\x1a\x99\x61\x70\xe6\x57\xa0\x5a\x26\x64\x26\xd9\x8c\x88\x00\x3f\x84\x77\xc1\x22\x70\x94\xa0\xd9\xfa\x1e\x8c\x40\x24\x30\x9c\xe1\xec\xcc\xb5\x21\x00\x35\xd4\x7a\xc7\x2e\x8a" + } + + , VectorOAEP -- 1.2 + { message = "\x75\x0c\x40\x47\xf5\x47\xe8\xe4\x14\x11\x85\x65\x23\x29\x8a\xc9\xba\xe2\x45\xef\xaf\x13\x97\xfb\xe5\x6f\x9d\xd5" + , seed = "\x0c\xc7\x42\xce\x4a\x9b\x7f\x32\xf9\x51\xbc\xb2\x51\xef\xd9\x25\xfe\x4f\xe3\x5f" + , cipherText = "\x64\x0d\xb1\xac\xc5\x8e\x05\x68\xfe\x54\x07\xe5\xf9\xb7\x01\xdf\xf8\xc3\xc9\x1e\x71\x6c\x53\x6f\xc7\xfc\xec\x6c\xb5\xb7\x1c\x11\x65\x98\x8d\x4a\x27\x9e\x15\x77\xd7\x30\xfc\x7a\x29\x93\x2e\x3f\x00\xc8\x15\x15\x23\x6d\x8d\x8e\x31\x01\x7a\x7a\x09\xdf\x43\x52\xd9\x04\xcd\xeb\x79\xaa\x58\x3a\xdc\xc3\x1e\xa6\x98\xa4\xc0\x52\x83\xda\xba\x90\x89\xbe\x54\x91\xf6\x7c\x1a\x4e\xe4\x8d\xc7\x4b\xbb\xe6\x64\x3a\xef\x84\x66\x79\xb4\xcb\x39\x5a\x35\x2d\x5e\xd1\x15\x91\x2d\xf6\x96\xff\xe0\x70\x29\x32\x94\x6d\x71\x49\x2b\x44" + } + , VectorOAEP -- 1.3 + { message = "\xd9\x4a\xe0\x83\x2e\x64\x45\xce\x42\x33\x1c\xb0\x6d\x53\x1a\x82\xb1\xdb\x4b\xaa\xd3\x0f\x74\x6d\xc9\x16\xdf\x24\xd4\xe3\xc2\x45\x1f\xff\x59\xa6\x42\x3e\xb0\xe1\xd0\x2d\x4f\xe6\x46\xcf\x69\x9d\xfd\x81\x8c\x6e\x97\xb0\x51" + , seed = "\x25\x14\xdf\x46\x95\x75\x5a\x67\xb2\x88\xea\xf4\x90\x5c\x36\xee\xc6\x6f\xd2\xfd" + , cipherText = "\x42\x37\x36\xed\x03\x5f\x60\x26\xaf\x27\x6c\x35\xc0\xb3\x74\x1b\x36\x5e\x5f\x76\xca\x09\x1b\x4e\x8c\x29\xe2\xf0\xbe\xfe\xe6\x03\x59\x5a\xa8\x32\x2d\x60\x2d\x2e\x62\x5e\x95\xeb\x81\xb2\xf1\xc9\x72\x4e\x82\x2e\xca\x76\xdb\x86\x18\xcf\x09\xc5\x34\x35\x03\xa4\x36\x08\x35\xb5\x90\x3b\xc6\x37\xe3\x87\x9f\xb0\x5e\x0e\xf3\x26\x85\xd5\xae\xc5\x06\x7c\xd7\xcc\x96\xfe\x4b\x26\x70\xb6\xea\xc3\x06\x6b\x1f\xcf\x56\x86\xb6\x85\x89\xaa\xfb\x7d\x62\x9b\x02\xd8\xf8\x62\x5c\xa3\x83\x36\x24\xd4\x80\x0f\xb0\x81\xb1\xcf\x94\xeb" + } + , VectorOAEP + { message = "\x52\xe6\x50\xd9\x8e\x7f\x2a\x04\x8b\x4f\x86\x85\x21\x53\xb9\x7e\x01\xdd\x31\x6f\x34\x6a\x19\xf6\x7a\x85" + , seed = "\xc4\x43\x5a\x3e\x1a\x18\xa6\x8b\x68\x20\x43\x62\x90\xa3\x7c\xef\xb8\x5d\xb3\xfb" + , cipherText = "\x45\xea\xd4\xca\x55\x1e\x66\x2c\x98\x00\xf1\xac\xa8\x28\x3b\x05\x25\xe6\xab\xae\x30\xbe\x4b\x4a\xba\x76\x2f\xa4\x0f\xd3\xd3\x8e\x22\xab\xef\xc6\x97\x94\xf6\xeb\xbb\xc0\x5d\xdb\xb1\x12\x16\x24\x7d\x2f\x41\x2f\xd0\xfb\xa8\x7c\x6e\x3a\xcd\x88\x88\x13\x64\x6f\xd0\xe4\x8e\x78\x52\x04\xf9\xc3\xf7\x3d\x6d\x82\x39\x56\x27\x22\xdd\xdd\x87\x71\xfe\xc4\x8b\x83\xa3\x1e\xe6\xf5\x92\xc4\xcf\xd4\xbc\x88\x17\x4f\x3b\x13\xa1\x12\xaa\xe3\xb9\xf7\xb8\x0e\x0f\xc6\xf7\x25\x5b\xa8\x80\xdc\x7d\x80\x21\xe2\x2a\xd6\xa8\x5f\x07\x55" + } + + , VectorOAEP + { message = "\x8d\xa8\x9f\xd9\xe5\xf9\x74\xa2\x9f\xef\xfb\x46\x2b\x49\x18\x0f\x6c\xf9\xe8\x02" + , seed = "\xb3\x18\xc4\x2d\xf3\xbe\x0f\x83\xfe\xa8\x23\xf5\xa7\xb4\x7e\xd5\xe4\x25\xa3\xb5" + , cipherText = "\x36\xf6\xe3\x4d\x94\xa8\xd3\x4d\xaa\xcb\xa3\x3a\x21\x39\xd0\x0a\xd8\x5a\x93\x45\xa8\x60\x51\xe7\x30\x71\x62\x00\x56\xb9\x20\xe2\x19\x00\x58\x55\xa2\x13\xa0\xf2\x38\x97\xcd\xcd\x73\x1b\x45\x25\x7c\x77\x7f\xe9\x08\x20\x2b\xef\xdd\x0b\x58\x38\x6b\x12\x44\xea\x0c\xf5\x39\xa0\x5d\x5d\x10\x32\x9d\xa4\x4e\x13\x03\x0f\xd7\x60\xdc\xd6\x44\xcf\xef\x20\x94\xd1\x91\x0d\x3f\x43\x3e\x1c\x7c\x6d\xd1\x8b\xc1\xf2\xdf\x7f\x64\x3d\x66\x2f\xb9\xdd\x37\xea\xd9\x05\x91\x90\xf4\xfa\x66\xca\x39\xe8\x69\xc4\xeb\x44\x9c\xbd\xc4\x39" + } + , VectorOAEP -- 1.6 + { message = "\x26\x52\x10\x50\x84\x42\x71" + , seed = "\xe4\xec\x09\x82\xc2\x33\x6f\x3a\x67\x7f\x6a\x35\x61\x74\xeb\x0c\xe8\x87\xab\xc2" + , cipherText = "\x42\xce\xe2\x61\x7b\x1e\xce\xa4\xdb\x3f\x48\x29\x38\x6f\xbd\x61\xda\xfb\xf0\x38\xe1\x80\xd8\x37\xc9\x63\x66\xdf\x24\xc0\x97\xb4\xab\x0f\xac\x6b\xdf\x59\x0d\x82\x1c\x9f\x10\x64\x2e\x68\x1a\xd0\x5b\x8d\x78\xb3\x78\xc0\xf4\x6c\xe2\xfa\xd6\x3f\x74\xe0\xad\x3d\xf0\x6b\x07\x5d\x7e\xb5\xf5\x63\x6f\x8d\x40\x3b\x90\x59\xca\x76\x1b\x5c\x62\xbb\x52\xaa\x45\x00\x2e\xa7\x0b\xaa\xce\x08\xde\xd2\x43\xb9\xd8\xcb\xd6\x2a\x68\xad\xe2\x65\x83\x2b\x56\x56\x4e\x43\xa6\xfa\x42\xed\x19\x9a\x09\x97\x69\x74\x2d\xf1\x53\x9e\x82\x55" + } + ] + + +doEncryptionTest key (i, vector) = testCase (show i) (Right (cipherText vector) @=? actual) + where actual = OAEP.encryptWithSeed (seed vector) (OAEP.defaultOAEPParams SHA1.hash) key (message vector) + +doDecryptionTest key (i, vector) = testCase (show i) (Right (message vector) @=? actual) + where actual = OAEP.decrypt Nothing (OAEP.defaultOAEPParams SHA1.hash) key (cipherText vector) + +oaepTests = testGroup "RSA-OAEP" + [ testGroup "internal" + [ doEncryptionTest (private_pub rsaKeyInt) (0, vectorInt) + , doDecryptionTest rsaKeyInt (0, vectorInt) + ] + , testGroup "encryption key 1024 bits" $ map (doEncryptionTest $ private_pub rsaKey1) (zip [0..] vectorsKey1) + , testGroup "decryption key 1024 bits" $ map (doDecryptionTest rsaKey1) (zip [0..] vectorsKey1) + ] diff --git a/tests/KAT_PubKey/PSS.hs b/tests/KAT_PubKey/PSS.hs new file mode 100644 index 0000000..a85673d --- /dev/null +++ b/tests/KAT_PubKey/PSS.hs @@ -0,0 +1,480 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_PubKey.PSS (pssTests) where + +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as BC + +import Crypto.PubKey.RSA +import Crypto.PubKey.MaskGenFunction +import qualified Crypto.PubKey.RSA.PSS as PSS +import qualified Crypto.Hash.SHA1 as SHA1 + +import Test.Tasty +import Test.Tasty.HUnit + +data VectorPSS = VectorPSS { message :: ByteString + , salt :: ByteString + , signature :: ByteString + } + +rsaKeyInt = PrivateKey + { private_pub = PublicKey + { public_n = 0xa2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5 + , public_e = 0x010001 + , public_size = 128 + } + , private_d = 0x50e2c3e38d886110288dfc68a9533e7e12e27d2aa56d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd35f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f4947b129151b71 + , private_p = 0xd17f655bf27c8b16d35462c905cc04a26f37e2a67fa9c0ce0dced472394a0df743fe7f929e378efdb368eddff453cf007af6d948e0ade757371f8a711e278f6b + , private_q = 0xc6d92b6fee7414d1358ce1546fb62987530b90bd15e0f14963a5e2635adb69347ec0c01b2ab1763fd8ac1a592fb22757463a982425bb97a3a437c5bf86d03f2f + , private_dP = 0x9d0dbf83e5ce9e4b1754dcd5cd05bcb7b55f1508330ea49f14d4e889550f8256cb5f806dff34b17ada44208853577d08e4262890acf752461cea05547601bc4f + , private_dQ = 0x1291a524c6b7c059e90e46dc83b2171eb3fa98818fd179b6c8bf6cecaa476303abf283fe05769cfc495788fe5b1ddfde9e884a3cd5e936b7e955ebf97eb563b1 + , private_qinv = 0xa63f1da38b950c9ad1c67ce0d677ec2914cd7d40062df42a67eb198a176f9742aac7c5fea14f2297662b84812c4defc49a8025ab4382286be4c03788dd01d69f + } + +rsaKey1 = PrivateKey + { private_pub = PublicKey + { public_n = 0xa56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137 + , public_e = 0x010001 + , public_size = 128 + } + , private_d = 0x33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325 + , private_p = 0xe7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443 + , private_q = 0xb69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd + , private_dP = 0x28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979 + , private_dQ = 0x1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729 + , private_qinv = 0x27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d + } + +vectorInt = VectorPSS + { message = "\x85\x9e\xef\x2f\xd7\x8a\xca\x00\x30\x8b\xdc\x47\x11\x93\xbf\x55\xbf\x9d\x78\xdb\x8f\x8a\x67\x2b\x48\x46\x34\xf3\xc9\xc2\x6e\x64\x78\xae\x10\x26\x0f\xe0\xdd\x8c\x08\x2e\x53\xa5\x29\x3a\xf2\x17\x3c\xd5\x0c\x6d\x5d\x35\x4f\xeb\xf7\x8b\x26\x02\x1c\x25\xc0\x27\x12\xe7\x8c\xd4\x69\x4c\x9f\x46\x97\x77\xe4\x51\xe7\xf8\xe9\xe0\x4c\xd3\x73\x9c\x6b\xbf\xed\xae\x48\x7f\xb5\x56\x44\xe9\xca\x74\xff\x77\xa5\x3c\xb7\x29\x80\x2f\x6e\xd4\xa5\xff\xa8\xba\x15\x98\x90\xfc" + , salt = "\xe3\xb5\xd5\xd0\x02\xc1\xbc\xe5\x0c\x2b\x65\xef\x88\xa1\x88\xd8\x3b\xce\x7e\x61" + , signature = "\x8d\xaa\x62\x7d\x3d\xe7\x59\x5d\x63\x05\x6c\x7e\xc6\x59\xe5\x44\x06\xf1\x06\x10\x12\x8b\xaa\xe8\x21\xc8\xb2\xa0\xf3\x93\x6d\x54\xdc\x3b\xdc\xe4\x66\x89\xf6\xb7\x95\x1b\xb1\x8e\x84\x05\x42\x76\x97\x18\xd5\x71\x5d\x21\x0d\x85\xef\xbb\x59\x61\x92\x03\x2c\x42\xbe\x4c\x29\x97\x2c\x85\x62\x75\xeb\x6d\x5a\x45\xf0\x5f\x51\x87\x6f\xc6\x74\x3d\xed\xdd\x28\xca\xec\x9b\xb3\x0e\xa9\x9e\x02\xc3\x48\x82\x69\x60\x4f\xe4\x97\xf7\x4c\xcd\x7c\x7f\xca\x16\x71\x89\x71\x23\xcb\xd3\x0d\xef\x5d\x54\xa2\xb5\x53\x6a\xd9\x0a\x74\x7e" + } + +{- +# mHash = Hash(M) +# salt = random string of octets +# M' = Padding || mHash || salt +# H = Hash(M') +# DB = Padding || salt +# dbMask = MGF(H, length(DB)) +# maskedDB = DB xor dbMask (leftmost bit set to +# zero) +# EM = maskedDB || H || 0xbc + +# mHash: +37 b6 6a e0 44 58 43 35 3d 47 ec b0 b4 fd 14 c1 +10 e6 2d 6a + +# salt: + +# M': +00 00 00 00 00 00 00 00 37 b6 6a e0 44 58 43 35 +3d 47 ec b0 b4 fd 14 c1 10 e6 2d 6a e3 b5 d5 d0 +02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8 3b ce 7e 61 + +# H: +df 1a 89 6f 9d 8b c8 16 d9 7c d7 a2 c4 3b ad 54 +6f be 8c fe + +# DB: +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 01 e3 b5 d5 d0 02 c1 bc e5 0c +2b 65 ef 88 a1 88 d8 3b ce 7e 61 + +# dbMask: +66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67 +d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af +50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4 +d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1 +e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec +d3 18 3a 31 1f c8 97 39 a9 66 43 13 6e 8b 0f 46 +5e 87 a4 53 5c d4 c5 9b 10 02 8d + +# maskedDB: +66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67 +d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af +50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4 +d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1 +e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec +d3 18 3a 31 1f c8 96 da 1c b3 93 11 af 37 ea 4a +75 e2 4b db fd 5c 1d a0 de 7c ec + +# Encoded message EM: +66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67 +d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af +50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4 +d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1 +e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec +d3 18 3a 31 1f c8 96 da 1c b3 93 11 af 37 ea 4a +75 e2 4b db fd 5c 1d a0 de 7c ec df 1a 89 6f 9d +8b c8 16 d9 7c d7 a2 c4 3b ad 54 6f be 8c fe bc +-} + +vectorsKey1 = + [ + -- Example 1.1 + VectorPSS + { message = "\xcd\xc8\x7d\xa2\x23\xd7\x86\xdf\x3b\x45\xe0\xbb\xbc\x72\x13\x26\xd1\xee\x2a\xf8\x06\xcc\x31\x54\x75\xcc\x6f\x0d\x9c\x66\xe1\xb6\x23\x71\xd4\x5c\xe2\x39\x2e\x1a\xc9\x28\x44\xc3\x10\x10\x2f\x15\x6a\x0d\x8d\x52\xc1\xf4\xc4\x0b\xa3\xaa\x65\x09\x57\x86\xcb\x76\x97\x57\xa6\x56\x3b\xa9\x58\xfe\xd0\xbc\xc9\x84\xe8\xb5\x17\xa3\xd5\xf5\x15\xb2\x3b\x8a\x41\xe7\x4a\xa8\x67\x69\x3f\x90\xdf\xb0\x61\xa6\xe8\x6d\xfa\xae\xe6\x44\x72\xc0\x0e\x5f\x20\x94\x57\x29\xcb\xeb\xe7\x7f\x06\xce\x78\xe0\x8f\x40\x98\xfb\xa4\x1f\x9d\x61\x93\xc0\x31\x7e\x8b\x60\xd4\xb6\x08\x4a\xcb\x42\xd2\x9e\x38\x08\xa3\xbc\x37\x2d\x85\xe3\x31\x17\x0f\xcb\xf7\xcc\x72\xd0\xb7\x1c\x29\x66\x48\xb3\xa4\xd1\x0f\x41\x62\x95\xd0\x80\x7a\xa6\x25\xca\xb2\x74\x4f\xd9\xea\x8f\xd2\x23\xc4\x25\x37\x02\x98\x28\xbd\x16\xbe\x02\x54\x6f\x13\x0f\xd2\xe3\x3b\x93\x6d\x26\x76\xe0\x8a\xed\x1b\x73\x31\x8b\x75\x0a\x01\x67\xd0" + , salt = "\xde\xe9\x59\xc7\xe0\x64\x11\x36\x14\x20\xff\x80\x18\x5e\xd5\x7f\x3e\x67\x76\xaf" + , signature = "\x90\x74\x30\x8f\xb5\x98\xe9\x70\x1b\x22\x94\x38\x8e\x52\xf9\x71\xfa\xac\x2b\x60\xa5\x14\x5a\xf1\x85\xdf\x52\x87\xb5\xed\x28\x87\xe5\x7c\xe7\xfd\x44\xdc\x86\x34\xe4\x07\xc8\xe0\xe4\x36\x0b\xc2\x26\xf3\xec\x22\x7f\x9d\x9e\x54\x63\x8e\x8d\x31\xf5\x05\x12\x15\xdf\x6e\xbb\x9c\x2f\x95\x79\xaa\x77\x59\x8a\x38\xf9\x14\xb5\xb9\xc1\xbd\x83\xc4\xe2\xf9\xf3\x82\xa0\xd0\xaa\x35\x42\xff\xee\x65\x98\x4a\x60\x1b\xc6\x9e\xb2\x8d\xeb\x27\xdc\xa1\x2c\x82\xc2\xd4\xc3\xf6\x6c\xd5\x00\xf1\xff\x2b\x99\x4d\x8a\x4e\x30\xcb\xb3\x3c" + } + -- Example 1.2 + , VectorPSS + { message = "\x85\x13\x84\xcd\xfe\x81\x9c\x22\xed\x6c\x4c\xcb\x30\xda\xeb\x5c\xf0\x59\xbc\x8e\x11\x66\xb7\xe3\x53\x0c\x4c\x23\x3e\x2b\x5f\x8f\x71\xa1\xcc\xa5\x82\xd4\x3e\xcc\x72\xb1\xbc\xa1\x6d\xfc\x70\x13\x22\x6b\x9e" + , salt = "\xef\x28\x69\xfa\x40\xc3\x46\xcb\x18\x3d\xab\x3d\x7b\xff\xc9\x8f\xd5\x6d\xf4\x2d" + , signature = "\x3e\xf7\xf4\x6e\x83\x1b\xf9\x2b\x32\x27\x41\x42\xa5\x85\xff\xce\xfb\xdc\xa7\xb3\x2a\xe9\x0d\x10\xfb\x0f\x0c\x72\x99\x84\xf0\x4e\xf2\x9a\x9d\xf0\x78\x07\x75\xce\x43\x73\x9b\x97\x83\x83\x90\xdb\x0a\x55\x05\xe6\x3d\xe9\x27\x02\x8d\x9d\x29\xb2\x19\xca\x2c\x45\x17\x83\x25\x58\xa5\x5d\x69\x4a\x6d\x25\xb9\xda\xb6\x60\x03\xc4\xcc\xcd\x90\x78\x02\x19\x3b\xe5\x17\x0d\x26\x14\x7d\x37\xb9\x35\x90\x24\x1b\xe5\x1c\x25\x05\x5f\x47\xef\x62\x75\x2c\xfb\xe2\x14\x18\xfa\xfe\x98\xc2\x2c\x4d\x4d\x47\x72\x4f\xdb\x56\x69\xe8\x43" + } + -- Example 1.3 + , VectorPSS + { message = "\xa4\xb1\x59\x94\x17\x61\xc4\x0c\x6a\x82\xf2\xb8\x0d\x1b\x94\xf5\xaa\x26\x54\xfd\x17\xe1\x2d\x58\x88\x64\x67\x9b\x54\xcd\x04\xef\x8b\xd0\x30\x12\xbe\x8d\xc3\x7f\x4b\x83\xaf\x79\x63\xfa\xff\x0d\xfa\x22\x54\x77\x43\x7c\x48\x01\x7f\xf2\xbe\x81\x91\xcf\x39\x55\xfc\x07\x35\x6e\xab\x3f\x32\x2f\x7f\x62\x0e\x21\xd2\x54\xe5\xdb\x43\x24\x27\x9f\xe0\x67\xe0\x91\x0e\x2e\x81\xca\x2c\xab\x31\xc7\x45\xe6\x7a\x54\x05\x8e\xb5\x0d\x99\x3c\xdb\x9e\xd0\xb4\xd0\x29\xc0\x6d\x21\xa9\x4c\xa6\x61\xc3\xce\x27\xfa\xe1\xd6\xcb\x20\xf4\x56\x4d\x66\xce\x47\x67\x58\x3d\x0e\x5f\x06\x02\x15\xb5\x90\x17\xbe\x85\xea\x84\x89\x39\x12\x7b\xd8\xc9\xc4\xd4\x7b\x51\x05\x6c\x03\x1c\xf3\x36\xf1\x7c\x99\x80\xf3\xb8\xf5\xb9\xb6\x87\x8e\x8b\x79\x7a\xa4\x3b\x88\x26\x84\x33\x3e\x17\x89\x3f\xe9\xca\xa6\xaa\x29\x9f\x7e\xd1\xa1\x8e\xe2\xc5\x48\x64\xb7\xb2\xb9\x9b\x72\x61\x8f\xb0\x25\x74\xd1\x39\xef\x50\xf0\x19\xc9\xee\xf4\x16\x97\x13\x38\xe7\xd4\x70" + , salt = "\x71\x0b\x9c\x47\x47\xd8\x00\xd4\xde\x87\xf1\x2a\xfd\xce\x6d\xf1\x81\x07\xcc\x77" + , signature = "\x66\x60\x26\xfb\xa7\x1b\xd3\xe7\xcf\x13\x15\x7c\xc2\xc5\x1a\x8e\x4a\xa6\x84\xaf\x97\x78\xf9\x18\x49\xf3\x43\x35\xd1\x41\xc0\x01\x54\xc4\x19\x76\x21\xf9\x62\x4a\x67\x5b\x5a\xbc\x22\xee\x7d\x5b\xaa\xff\xaa\xe1\xc9\xba\xca\x2c\xc3\x73\xb3\xf3\x3e\x78\xe6\x14\x3c\x39\x5a\x91\xaa\x7f\xac\xa6\x64\xeb\x73\x3a\xfd\x14\xd8\x82\x72\x59\xd9\x9a\x75\x50\xfa\xca\x50\x1e\xf2\xb0\x4e\x33\xc2\x3a\xa5\x1f\x4b\x9e\x82\x82\xef\xdb\x72\x8c\xc0\xab\x09\x40\x5a\x91\x60\x7c\x63\x69\x96\x1b\xc8\x27\x0d\x2d\x4f\x39\xfc\xe6\x12\xb1" + } + -- Example 1.4 + , VectorPSS + { message = "\xbc\x65\x67\x47\xfa\x9e\xaf\xb3\xf0" + , salt = "\x05\x6f\x00\x98\x5d\xe1\x4d\x8e\xf5\xce\xa9\xe8\x2f\x8c\x27\xbe\xf7\x20\x33\x5e" + , signature = "\x46\x09\x79\x3b\x23\xe9\xd0\x93\x62\xdc\x21\xbb\x47\xda\x0b\x4f\x3a\x76\x22\x64\x9a\x47\xd4\x64\x01\x9b\x9a\xea\xfe\x53\x35\x9c\x17\x8c\x91\xcd\x58\xba\x6b\xcb\x78\xbe\x03\x46\xa7\xbc\x63\x7f\x4b\x87\x3d\x4b\xab\x38\xee\x66\x1f\x19\x96\x34\xc5\x47\xa1\xad\x84\x42\xe0\x3d\xa0\x15\xb1\x36\xe5\x43\xf7\xab\x07\xc0\xc1\x3e\x42\x25\xb8\xde\x8c\xce\x25\xd4\xf6\xeb\x84\x00\xf8\x1f\x7e\x18\x33\xb7\xee\x6e\x33\x4d\x37\x09\x64\xca\x79\xfd\xb8\x72\xb4\xd7\x52\x23\xb5\xee\xb0\x81\x01\x59\x1f\xb5\x32\xd1\x55\xa6\xde\x87" + } + -- Example 1.5 + , VectorPSS + { message = "\xb4\x55\x81\x54\x7e\x54\x27\x77\x0c\x76\x8e\x8b\x82\xb7\x55\x64\xe0\xea\x4e\x9c\x32\x59\x4d\x6b\xff\x70\x65\x44\xde\x0a\x87\x76\xc7\xa8\x0b\x45\x76\x55\x0e\xee\x1b\x2a\xca\xbc\x7e\x8b\x7d\x3e\xf7\xbb\x5b\x03\xe4\x62\xc1\x10\x47\xea\xdd\x00\x62\x9a\xe5\x75\x48\x0a\xc1\x47\x0f\xe0\x46\xf1\x3a\x2b\xf5\xaf\x17\x92\x1d\xc4\xb0\xaa\x8b\x02\xbe\xe6\x33\x49\x11\x65\x1d\x7f\x85\x25\xd1\x0f\x32\xb5\x1d\x33\xbe\x52\x0d\x3d\xdf\x5a\x70\x99\x55\xa3\xdf\xe7\x82\x83\xb9\xe0\xab\x54\x04\x6d\x15\x0c\x17\x7f\x03\x7f\xdc\xcc\x5b\xe4\xea\x5f\x68\xb5\xe5\xa3\x8c\x9d\x7e\xdc\xcc\xc4\x97\x5f\x45\x5a\x69\x09\xb4" + , salt = "\x80\xe7\x0f\xf8\x6a\x08\xde\x3e\xc6\x09\x72\xb3\x9b\x4f\xbf\xdc\xea\x67\xae\x8e" + , signature = "\x1d\x2a\xad\x22\x1c\xa4\xd3\x1d\xdf\x13\x50\x92\x39\x01\x93\x98\xe3\xd1\x4b\x32\xdc\x34\xdc\x5a\xf4\xae\xae\xa3\xc0\x95\xaf\x73\x47\x9c\xf0\xa4\x5e\x56\x29\x63\x5a\x53\xa0\x18\x37\x76\x15\xb1\x6c\xb9\xb1\x3b\x3e\x09\xd6\x71\xeb\x71\xe3\x87\xb8\x54\x5c\x59\x60\xda\x5a\x64\x77\x6e\x76\x8e\x82\xb2\xc9\x35\x83\xbf\x10\x4c\x3f\xdb\x23\x51\x2b\x7b\x4e\x89\xf6\x33\xdd\x00\x63\xa5\x30\xdb\x45\x24\xb0\x1c\x3f\x38\x4c\x09\x31\x0e\x31\x5a\x79\xdc\xd3\xd6\x84\x02\x2a\x7f\x31\xc8\x65\xa6\x64\xe3\x16\x97\x8b\x75\x9f\xad" + } + -- Example 1.6 + , VectorPSS + { message = "\x10\xaa\xe9\xa0\xab\x0b\x59\x5d\x08\x41\x20\x7b\x70\x0d\x48\xd7\x5f\xae\xdd\xe3\xb7\x75\xcd\x6b\x4c\xc8\x8a\xe0\x6e\x46\x94\xec\x74\xba\x18\xf8\x52\x0d\x4f\x5e\xa6\x9c\xbb\xe7\xcc\x2b\xeb\xa4\x3e\xfd\xc1\x02\x15\xac\x4e\xb3\x2d\xc3\x02\xa1\xf5\x3d\xc6\xc4\x35\x22\x67\xe7\x93\x6c\xfe\xbf\x7c\x8d\x67\x03\x57\x84\xa3\x90\x9f\xa8\x59\xc7\xb7\xb5\x9b\x8e\x39\xc5\xc2\x34\x9f\x18\x86\xb7\x05\xa3\x02\x67\xd4\x02\xf7\x48\x6a\xb4\xf5\x8c\xad\x5d\x69\xad\xb1\x7a\xb8\xcd\x0c\xe1\xca\xf5\x02\x5a\xf4\xae\x24\xb1\xfb\x87\x94\xc6\x07\x0c\xc0\x9a\x51\xe2\xf9\x91\x13\x11\xe3\x87\x7d\x00\x44\xc7\x1c\x57\xa9\x93\x39\x50\x08\x80\x6b\x72\x3a\xc3\x83\x73\xd3\x95\x48\x18\x18\x52\x8c\x1e\x70\x53\x73\x92\x82\x05\x35\x29\x51\x0e\x93\x5c\xd0\xfa\x77\xb8\xfa\x53\xcc\x2d\x47\x4b\xd4\xfb\x3c\xc5\xc6\x72\xd6\xff\xdc\x90\xa0\x0f\x98\x48\x71\x2c\x4b\xcf\xe4\x6c\x60\x57\x36\x59\xb1\x1e\x64\x57\xe8\x61\xf0\xf6\x04\xb6\x13\x8d\x14\x4f\x8c\xe4\xe2\xda\x73" + , salt = "\xa8\xab\x69\xdd\x80\x1f\x00\x74\xc2\xa1\xfc\x60\x64\x98\x36\xc6\x16\xd9\x96\x81" + , signature = "\x2a\x34\xf6\x12\x5e\x1f\x6b\x0b\xf9\x71\xe8\x4f\xbd\x41\xc6\x32\xbe\x8f\x2c\x2a\xce\x7d\xe8\xb6\x92\x6e\x31\xff\x93\xe9\xaf\x98\x7f\xbc\x06\xe5\x1e\x9b\xe1\x4f\x51\x98\xf9\x1f\x3f\x95\x3b\xd6\x7d\xa6\x0a\x9d\xf5\x97\x64\xc3\xdc\x0f\xe0\x8e\x1c\xbe\xf0\xb7\x5f\x86\x8d\x10\xad\x3f\xba\x74\x9f\xef\x59\xfb\x6d\xac\x46\xa0\xd6\xe5\x04\x36\x93\x31\x58\x6f\x58\xe4\x62\x8f\x39\xaa\x27\x89\x82\x54\x3b\xc0\xee\xb5\x37\xdc\x61\x95\x80\x19\xb3\x94\xfb\x27\x3f\x21\x58\x58\xa0\xa0\x1a\xc4\xd6\x50\xb9\x55\xc6\x7f\x4c\x58" + } + ] + +{- +# =================================== +# Example 10: A 2048-bit RSA Key Pair +# =================================== + +# ------------------------------ +# Components of the RSA Key Pair +# ------------------------------ + +# RSA modulus n: +a5 dd 86 7a c4 cb 02 f9 0b 94 57 d4 8c 14 a7 70 +ef 99 1c 56 c3 9c 0e c6 5f d1 1a fa 89 37 ce a5 +7b 9b e7 ac 73 b4 5c 00 17 61 5b 82 d6 22 e3 18 +75 3b 60 27 c0 fd 15 7b e1 2f 80 90 fe e2 a7 ad +cd 0e ef 75 9f 88 ba 49 97 c7 a4 2d 58 c9 aa 12 +cb 99 ae 00 1f e5 21 c1 3b b5 43 14 45 a8 d5 ae +4f 5e 4c 7e 94 8a c2 27 d3 60 40 71 f2 0e 57 7e +90 5f be b1 5d fa f0 6d 1d e5 ae 62 53 d6 3a 6a +21 20 b3 1a 5d a5 da bc 95 50 60 0e 20 f2 7d 37 +39 e2 62 79 25 fe a3 cc 50 9f 21 df f0 4e 6e ea +45 49 c5 40 d6 80 9f f9 30 7e ed e9 1f ff 58 73 +3d 83 85 a2 37 d6 d3 70 5a 33 e3 91 90 09 92 07 +0d f7 ad f1 35 7c f7 e3 70 0c e3 66 7d e8 3f 17 +b8 df 17 78 db 38 1d ce 09 cb 4a d0 58 a5 11 00 +1a 73 81 98 ee 27 cf 55 a1 3b 75 45 39 90 65 82 +ec 8b 17 4b d5 8d 5d 1f 3d 76 7c 61 37 21 ae 05 + +# RSA public exponent e: +01 00 01 + +# RSA private exponent d: +2d 2f f5 67 b3 fe 74 e0 61 91 b7 fd ed 6d e1 12 +29 0c 67 06 92 43 0d 59 69 18 40 47 da 23 4c 96 +93 de ed 16 73 ed 42 95 39 c9 69 d3 72 c0 4d 6b +47 e0 f5 b8 ce e0 84 3e 5c 22 83 5d bd 3b 05 a0 +99 79 84 ae 60 58 b1 1b c4 90 7c bf 67 ed 84 fa +9a e2 52 df b0 d0 cd 49 e6 18 e3 5d fd fe 59 bc +a3 dd d6 6c 33 ce bb c7 7a d4 41 aa 69 5e 13 e3 +24 b5 18 f0 1c 60 f5 a8 5c 99 4a d1 79 f2 a6 b5 +fb e9 34 02 b1 17 67 be 01 bf 07 34 44 d6 ba 1d +d2 bc a5 bd 07 4d 4a 5f ae 35 31 ad 13 03 d8 4b +30 d8 97 31 8c bb ba 04 e0 3c 2e 66 de 6d 91 f8 +2f 96 ea 1d 4b b5 4a 5a ae 10 2d 59 46 57 f5 c9 +78 95 53 51 2b 29 6d ea 29 d8 02 31 96 35 7e 3e +3a 6e 95 8f 39 e3 c2 34 40 38 ea 60 4b 31 ed c6 +f0 f7 ff 6e 71 81 a5 7c 92 82 6a 26 8f 86 76 8e +96 f8 78 56 2f c7 1d 85 d6 9e 44 86 12 f7 04 8f + +# Prime p: +cf d5 02 83 fe ee b9 7f 6f 08 d7 3c bc 7b 38 36 +f8 2b bc d4 99 47 9f 5e 6f 76 fd fc b8 b3 8c 4f +71 dc 9e 88 bd 6a 6f 76 37 1a fd 65 d2 af 18 62 +b3 2a fb 34 a9 5f 71 b8 b1 32 04 3f fe be 3a 95 +2b af 75 92 44 81 48 c0 3f 9c 69 b1 d6 8e 4c e5 +cf 32 c8 6b af 46 fe d3 01 ca 1a b4 03 06 9b 32 +f4 56 b9 1f 71 89 8a b0 81 cd 8c 42 52 ef 52 71 +91 5c 97 94 b8 f2 95 85 1d a7 51 0f 99 cb 73 eb + +# Prime q: +cc 4e 90 d2 a1 b3 a0 65 d3 b2 d1 f5 a8 fc e3 1b +54 44 75 66 4e ab 56 1d 29 71 b9 9f b7 be f8 44 +e8 ec 1f 36 0b 8c 2a c8 35 96 92 97 1e a6 a3 8f +72 3f cc 21 1f 5d bc b1 77 a0 fd ac 51 64 a1 d4 +ff 7f bb 4e 82 99 86 35 3c b9 83 65 9a 14 8c dd +42 0c 7d 31 ba 38 22 ea 90 a3 2b e4 6c 03 0e 8c +17 e1 fa 0a d3 78 59 e0 6b 0a a6 fa 3b 21 6d 9c +be 6c 0e 22 33 97 69 c0 a6 15 91 3e 5d a7 19 cf + +# p's CRT exponent dP: +1c 2d 1f c3 2f 6b c4 00 4f d8 5d fd e0 fb bf 9a +4c 38 f9 c7 c4 e4 1d ea 1a a8 82 34 a2 01 cd 92 +f3 b7 da 52 65 83 a9 8a d8 5b b3 60 fb 98 3b 71 +1e 23 44 9d 56 1d 17 78 d7 a5 15 48 6b cb f4 7b +46 c9 e9 e1 a3 a1 f7 70 00 ef be b0 9a 8a fe 47 +e5 b8 57 cd a9 9c b1 6d 7f ff 9b 71 2e 3b d6 0c +a9 6d 9c 79 73 d6 16 d4 69 34 a9 c0 50 28 1c 00 +43 99 ce ff 1d b7 dd a7 87 66 a8 a9 b9 cb 08 73 + +# q's CRT exponent dQ: +cb 3b 3c 04 ca a5 8c 60 be 7d 9b 2d eb b3 e3 96 +43 f4 f5 73 97 be 08 23 6a 1e 9e af aa 70 65 36 +e7 1c 3a cf e0 1c c6 51 f2 3c 9e 05 85 8f ee 13 +bb 6a 8a fc 47 df 4e dc 9a 4b a3 0b ce cb 73 d0 +15 78 52 32 7e e7 89 01 5c 2e 8d ee 7b 9f 05 a0 +f3 1a c9 4e b6 17 31 64 74 0c 5c 95 14 7c d5 f3 +b5 ae 2c b4 a8 37 87 f0 1d 8a b3 1f 27 c2 d0 ee +a2 dd 8a 11 ab 90 6a ba 20 7c 43 c6 ee 12 53 31 + +# CRT coefficient qInv: +12 f6 b2 cf 13 74 a7 36 fa d0 56 16 05 0f 96 ab +4b 61 d1 17 7c 7f 9d 52 5a 29 f3 d1 80 e7 76 67 +e9 9d 99 ab f0 52 5d 07 58 66 0f 37 52 65 5b 0f +25 b8 df 84 31 d9 a8 ff 77 c1 6c 12 a0 a5 12 2a +9f 0b f7 cf d5 a2 66 a3 5c 15 9f 99 12 08 b9 03 +16 ff 44 4f 3e 0b 6b d0 e9 3b 8a 7a 24 48 e9 57 +e3 dd a6 cf cf 22 66 b1 06 01 3a c4 68 08 d3 b3 +88 7b 3b 00 34 4b aa c9 53 0b 4c e7 08 fc 32 b6 + +# --------------------------------- +# RSASSA-PSS Signature Example 10.1 +# --------------------------------- + +# Message to be signed: +88 31 77 e5 12 6b 9b e2 d9 a9 68 03 27 d5 37 0c +6f 26 86 1f 58 20 c4 3d a6 7a 3a d6 09 + +# Salt: +04 e2 15 ee 6f f9 34 b9 da 70 d7 73 0c 87 34 ab +fc ec de 89 + +# Signature: +82 c2 b1 60 09 3b 8a a3 c0 f7 52 2b 19 f8 73 54 +06 6c 77 84 7a bf 2a 9f ce 54 2d 0e 84 e9 20 c5 +af b4 9f fd fd ac e1 65 60 ee 94 a1 36 96 01 14 +8e ba d7 a0 e1 51 cf 16 33 17 91 a5 72 7d 05 f2 +1e 74 e7 eb 81 14 40 20 69 35 d7 44 76 5a 15 e7 +9f 01 5c b6 6c 53 2c 87 a6 a0 59 61 c8 bf ad 74 +1a 9a 66 57 02 28 94 39 3e 72 23 73 97 96 c0 2a +77 45 5d 0f 55 5b 0e c0 1d df 25 9b 62 07 fd 0f +d5 76 14 ce f1 a5 57 3b aa ff 4e c0 00 69 95 16 +59 b8 5f 24 30 0a 25 16 0c a8 52 2d c6 e6 72 7e +57 d0 19 d7 e6 36 29 b8 fe 5e 89 e2 5c c1 5b eb +3a 64 75 77 55 92 99 28 0b 9b 28 f7 9b 04 09 00 +0b e2 5b bd 96 40 8b a3 b4 3c c4 86 18 4d d1 c8 +e6 25 53 fa 1a f4 04 0f 60 66 3d e7 f5 e4 9c 04 +38 8e 25 7f 1c e8 9c 95 da b4 8a 31 5d 9b 66 b1 +b7 62 82 33 87 6f f2 38 52 30 d0 70 d0 7e 16 66 + +# --------------------------------- +# RSASSA-PSS Signature Example 10.2 +# --------------------------------- + +# Message to be signed: +dd 67 0a 01 46 58 68 ad c9 3f 26 13 19 57 a5 0c +52 fb 77 7c db aa 30 89 2c 9e 12 36 11 64 ec 13 +97 9d 43 04 81 18 e4 44 5d b8 7b ee 58 dd 98 7b +34 25 d0 20 71 d8 db ae 80 70 8b 03 9d bb 64 db +d1 de 56 57 d9 fe d0 c1 18 a5 41 43 74 2e 0f f3 +c8 7f 74 e4 58 57 64 7a f3 f7 9e b0 a1 4c 9d 75 +ea 9a 1a 04 b7 cf 47 8a 89 7a 70 8f d9 88 f4 8e +80 1e db 0b 70 39 df 8c 23 bb 3c 56 f4 e8 21 ac + +# Salt: +8b 2b dd 4b 40 fa f5 45 c7 78 dd f9 bc 1a 49 cb +57 f9 b7 1b + +# Signature: +14 ae 35 d9 dd 06 ba 92 f7 f3 b8 97 97 8a ed 7c +d4 bf 5f f0 b5 85 a4 0b d4 6c e1 b4 2c d2 70 30 +53 bb 90 44 d6 4e 81 3d 8f 96 db 2d d7 00 7d 10 +11 8f 6f 8f 84 96 09 7a d7 5e 1f f6 92 34 1b 28 +92 ad 55 a6 33 a1 c5 5e 7f 0a 0a d5 9a 0e 20 3a +5b 82 78 ae c5 4d d8 62 2e 28 31 d8 71 74 f8 ca +ff 43 ee 6c 46 44 53 45 d8 4a 59 65 9b fb 92 ec +d4 c8 18 66 86 95 f3 47 06 f6 68 28 a8 99 59 63 +7f 2b f3 e3 25 1c 24 bd ba 4d 4b 76 49 da 00 22 +21 8b 11 9c 84 e7 9a 65 27 ec 5b 8a 5f 86 1c 15 +99 52 e2 3e c0 5e 1e 71 73 46 fa ef e8 b1 68 68 +25 bd 2b 26 2f b2 53 10 66 c0 de 09 ac de 2e 42 +31 69 07 28 b5 d8 5e 11 5a 2f 6b 92 b7 9c 25 ab +c9 bd 93 99 ff 8b cf 82 5a 52 ea 1f 56 ea 76 dd +26 f4 3b aa fa 18 bf a9 2a 50 4c bd 35 69 9e 26 +d1 dc c5 a2 88 73 85 f3 c6 32 32 f0 6f 32 44 c3 + +# --------------------------------- +# RSASSA-PSS Signature Example 10.3 +# --------------------------------- + +# Message to be signed: +48 b2 b6 a5 7a 63 c8 4c ea 85 9d 65 c6 68 28 4b +08 d9 6b dc aa be 25 2d b0 e4 a9 6c b1 ba c6 01 +93 41 db 6f be fb 8d 10 6b 0e 90 ed a6 bc c6 c6 +26 2f 37 e7 ea 9c 7e 5d 22 6b d7 df 85 ec 5e 71 +ef ff 2f 54 c5 db 57 7f f7 29 ff 91 b8 42 49 1d +e2 74 1d 0c 63 16 07 df 58 6b 90 5b 23 b9 1a f1 +3d a1 23 04 bf 83 ec a8 a7 3e 87 1f f9 db + +# Salt: +4e 96 fc 1b 39 8f 92 b4 46 71 01 0c 0d c3 ef d6 +e2 0c 2d 73 + +# Signature: +6e 3e 4d 7b 6b 15 d2 fb 46 01 3b 89 00 aa 5b bb +39 39 cf 2c 09 57 17 98 70 42 02 6e e6 2c 74 c5 +4c ff d5 d7 d5 7e fb bf 95 0a 0f 5c 57 4f a0 9d +3f c1 c9 f5 13 b0 5b 4f f5 0d d8 df 7e df a2 01 +02 85 4c 35 e5 92 18 01 19 a7 0c e5 b0 85 18 2a +a0 2d 9e a2 aa 90 d1 df 03 f2 da ae 88 5b a2 f5 +d0 5a fd ac 97 47 6f 06 b9 3b 5b c9 4a 1a 80 aa +91 16 c4 d6 15 f3 33 b0 98 89 2b 25 ff ac e2 66 +f5 db 5a 5a 3b cc 10 a8 24 ed 55 aa d3 5b 72 78 +34 fb 8c 07 da 28 fc f4 16 a5 d9 b2 22 4f 1f 8b +44 2b 36 f9 1e 45 6f de a2 d7 cf e3 36 72 68 de +03 07 a4 c7 4e 92 41 59 ed 33 39 3d 5e 06 55 53 +1c 77 32 7b 89 82 1b de df 88 01 61 c7 8c d4 19 +6b 54 19 f7 ac c3 f1 3e 5e bf 16 1b 6e 7c 67 24 +71 6c a3 3b 85 c2 e2 56 40 19 2a c2 85 96 51 d5 +0b de 7e b9 76 e5 1c ec 82 8b 98 b6 56 3b 86 bb + +# --------------------------------- +# RSASSA-PSS Signature Example 10.4 +# --------------------------------- + +# Message to be signed: +0b 87 77 c7 f8 39 ba f0 a6 4b bb db c5 ce 79 75 +5c 57 a2 05 b8 45 c1 74 e2 d2 e9 05 46 a0 89 c4 +e6 ec 8a df fa 23 a7 ea 97 ba e6 b6 5d 78 2b 82 +db 5d 2b 5a 56 d2 2a 29 a0 5e 7c 44 33 e2 b8 2a +62 1a bb a9 0a dd 05 ce 39 3f c4 8a 84 05 42 45 +1a + +# Salt: +c7 cd 69 8d 84 b6 51 28 d8 83 5e 3a 8b 1e b0 e0 +1c b5 41 ec + +# Signature: +34 04 7f f9 6c 4d c0 dc 90 b2 d4 ff 59 a1 a3 61 +a4 75 4b 25 5d 2e e0 af 7d 8b f8 7c 9b c9 e7 dd +ee de 33 93 4c 63 ca 1c 0e 3d 26 2c b1 45 ef 93 +2a 1f 2c 0a 99 7a a6 a3 4f 8e ae e7 47 7d 82 cc +f0 90 95 a6 b8 ac ad 38 d4 ee c9 fb 7e ab 7a d0 +2d a1 d1 1d 8e 54 c1 82 5e 55 bf 58 c2 a2 32 34 +b9 02 be 12 4f 9e 90 38 a8 f6 8f a4 5d ab 72 f6 +6e 09 45 bf 1d 8b ac c9 04 4c 6f 07 09 8c 9f ce +c5 8a 3a ab 10 0c 80 51 78 15 5f 03 0a 12 4c 45 +0e 5a cb da 47 d0 e4 f1 0b 80 a2 3f 80 3e 77 4d +02 3b 00 15 c2 0b 9f 9b be 7c 91 29 63 38 d5 ec +b4 71 ca fb 03 20 07 b6 7a 60 be 5f 69 50 4a 9f +01 ab b3 cb 46 7b 26 0e 2b ce 86 0b e8 d9 5b f9 +2c 0c 8e 14 96 ed 1e 52 85 93 a4 ab b6 df 46 2d +de 8a 09 68 df fe 46 83 11 68 57 a2 32 f5 eb f6 +c8 5b e2 38 74 5a d0 f3 8f 76 7a 5f db f4 86 fb + +# --------------------------------- +# RSASSA-PSS Signature Example 10.5 +# --------------------------------- + +# Message to be signed: +f1 03 6e 00 8e 71 e9 64 da dc 92 19 ed 30 e1 7f +06 b4 b6 8a 95 5c 16 b3 12 b1 ed df 02 8b 74 97 +6b ed 6b 3f 6a 63 d4 e7 78 59 24 3c 9c cc dc 98 +01 65 23 ab b0 24 83 b3 55 91 c3 3a ad 81 21 3b +b7 c7 bb 1a 47 0a ab c1 0d 44 25 6c 4d 45 59 d9 +16 + +# Salt: +ef a8 bf f9 62 12 b2 f4 a3 f3 71 a1 0d 57 41 52 +65 5f 5d fb + +# Signature: +7e 09 35 ea 18 f4 d6 c1 d1 7c e8 2e b2 b3 83 6c +55 b3 84 58 9c e1 9d fe 74 33 63 ac 99 48 d1 f3 +46 b7 bf dd fe 92 ef d7 8a db 21 fa ef c8 9a de +42 b1 0f 37 40 03 fe 12 2e 67 42 9a 1c b8 cb d1 +f8 d9 01 45 64 c4 4d 12 01 16 f4 99 0f 1a 6e 38 +77 4c 19 4b d1 b8 21 32 86 b0 77 b0 49 9d 2e 7b +3f 43 4a b1 22 89 c5 56 68 4d ee d7 81 31 93 4b +b3 dd 65 37 23 6f 7c 6f 3d cb 09 d4 76 be 07 72 +1e 37 e1 ce ed 9b 2f 7b 40 68 87 bd 53 15 73 05 +e1 c8 b4 f8 4d 73 3b c1 e1 86 fe 06 cc 59 b6 ed +b8 f4 bd 7f fe fd f4 f7 ba 9c fb 9d 57 06 89 b5 +a1 a4 10 9a 74 6a 69 08 93 db 37 99 25 5a 0c b9 +21 5d 2d 1c d4 90 59 0e 95 2e 8c 87 86 aa 00 11 +26 52 52 47 0c 04 1d fb c3 ee c7 c3 cb f7 1c 24 +86 9d 11 5c 0c b4 a9 56 f5 6d 53 0b 80 ab 58 9a +cf ef c6 90 75 1d df 36 e8 d3 83 f8 3c ed d2 cc + +# --------------------------------- +# RSASSA-PSS Signature Example 10.6 +# --------------------------------- + +# Message to be signed: +25 f1 08 95 a8 77 16 c1 37 45 0b b9 51 9d fa a1 +f2 07 fa a9 42 ea 88 ab f7 1e 9c 17 98 00 85 b5 +55 ae ba b7 62 64 ae 2a 3a b9 3c 2d 12 98 11 91 +dd ac 6f b5 94 9e b3 6a ee 3c 5d a9 40 f0 07 52 +c9 16 d9 46 08 fa 7d 97 ba 6a 29 15 b6 88 f2 03 +23 d4 e9 d9 68 01 d8 9a 72 ab 58 92 dc 21 17 c0 +74 34 fc f9 72 e0 58 cf 8c 41 ca 4b 4f f5 54 f7 +d5 06 8a d3 15 5f ce d0 f3 12 5b c0 4f 91 93 37 +8a 8f 5c 4c 3b 8c b4 dd 6d 1c c6 9d 30 ec ca 6e +aa 51 e3 6a 05 73 0e 9e 34 2e 85 5b af 09 9d ef +b8 af d7 + +# Salt: +ad 8b 15 23 70 36 46 22 4b 66 0b 55 08 85 91 7c +a2 d1 df 28 + +# Signature: +6d 3b 5b 87 f6 7e a6 57 af 21 f7 54 41 97 7d 21 +80 f9 1b 2c 5f 69 2d e8 29 55 69 6a 68 67 30 d9 +b9 77 8d 97 07 58 cc b2 60 71 c2 20 9f fb d6 12 +5b e2 e9 6e a8 1b 67 cb 9b 93 08 23 9f da 17 f7 +b2 b6 4e cd a0 96 b6 b9 35 64 0a 5a 1c b4 2a 91 +55 b1 c9 ef 7a 63 3a 02 c5 9f 0d 6e e5 9b 85 2c +43 b3 50 29 e7 3c 94 0f f0 41 0e 8f 11 4e ed 46 +bb d0 fa e1 65 e4 2b e2 52 8a 40 1c 3b 28 fd 81 +8e f3 23 2d ca 9f 4d 2a 0f 51 66 ec 59 c4 23 96 +d6 c1 1d bc 12 15 a5 6f a1 71 69 db 95 75 34 3e +f3 4f 9d e3 2a 49 cd c3 17 49 22 f2 29 c2 3e 18 +e4 5d f9 35 31 19 ec 43 19 ce dc e7 a1 7c 64 08 +8c 1f 6f 52 be 29 63 41 00 b3 91 9d 38 f3 d1 ed +94 e6 89 1e 66 a7 3b 8f b8 49 f5 87 4d f5 94 59 +e2 98 c7 bb ce 2e ee 78 2a 19 5a a6 6f e2 d0 73 +2b 25 e5 95 f5 7d 3e 06 1b 1f c3 e4 06 3b f9 8f + +-} + +doSignTest key (i, vector) = testCase (show i) (Right (signature vector) @=? actual) + where actual = PSS.signWithSalt (salt vector) Nothing PSS.defaultPSSParamsSHA1 key (message vector) + +doVerifyTest key (i, vector) = testCase (show i) (True @=? actual) + where actual = PSS.verify PSS.defaultPSSParamsSHA1 (private_pub key) (message vector) (signature vector) + +pssTests = testGroup "RSA-PSS" + [ testGroup "signature internal" + [ doSignTest rsaKeyInt (0, vectorInt) ] + , testGroup "verify internal" + [ doVerifyTest rsaKeyInt (0, vectorInt) ] + , testGroup "signature key 1024" $ map (doSignTest rsaKey1) (zip [0..] vectorsKey1) + , testGroup "verify key 1024" $ map (doVerifyTest rsaKey1) (zip [0..] vectorsKey1) + ] diff --git a/tests/Tests.hs b/tests/Tests.hs index cf44b19..2b3f09f 100644 --- a/tests/Tests.hs +++ b/tests/Tests.hs @@ -19,6 +19,7 @@ import qualified KATHash import qualified KAT_HMAC import qualified KAT_PBKDF2 import qualified KAT_Curve25519 +import qualified KAT_PubKey import qualified KAT_Scrypt import qualified KAT_RC4 import qualified KAT_Blowfish @@ -78,6 +79,7 @@ tests = testGroup "cryptonite" , KATHash.tests , KAT_HMAC.tests , KAT_Curve25519.tests + , KAT_PubKey.tests , KAT_PBKDF2.tests , KAT_Scrypt.tests , KAT_RC4.tests