From f9a0bc3c53f880a8622042f30ab014504378787e Mon Sep 17 00:00:00 2001 From: Baojun Wang Date: Mon, 15 May 2017 19:42:19 -0700 Subject: [PATCH 1/3] Allow sign/verify digest directly currently sign/verify works on message directly, it would be nice if PSS could sign/verify digest directly. This is useful for: 1) for some signing server it only has a digest (without message) 2) message could be very large, for cases when client need request a singing server to sign, it may make more sense for the client to compute digest, then ask server to (PSS) sign the digest 3) openSSL pkeyutl (PSS) sign operation signs with digest only, not the message, it would be nice to work with openSSL more easily *openSSL command line: ```shell openssl pkeyutl -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:-1 -pkeyopt digest:sha256 -sign -inkey "pri.key" -in hmac.bin > sig.bin openssl pkeyutl -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:-1 -pkeyopt digest:sha256 -verify -inkey "pri.key" -in hmac.bin -sigfile sig.bin ``` --- Crypto/PubKey/RSA/PSS.hs | 84 ++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/Crypto/PubKey/RSA/PSS.hs b/Crypto/PubKey/RSA/PSS.hs index c449a97..ab13b4d 100644 --- a/Crypto/PubKey/RSA/PSS.hs +++ b/Crypto/PubKey/RSA/PSS.hs @@ -11,9 +11,13 @@ module Crypto.PubKey.RSA.PSS , defaultPSSParamsSHA1 -- * Sign and verify functions , signWithSalt + , signDigestWithSalt , sign + , signDigest , signSafer + , signDigestSafer , verify + , verifyDigest ) where import Crypto.Random.Types @@ -27,6 +31,7 @@ import Data.Word import Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray) import qualified Crypto.Internal.ByteArray as B (convert) + import Data.ByteString (ByteString) import qualified Data.ByteString as B @@ -53,6 +58,32 @@ defaultPSSParams hashAlg = defaultPSSParamsSHA1 :: PSSParams SHA1 ByteString ByteString defaultPSSParamsSHA1 = defaultPSSParams SHA1 +-- | Sign using the PSS parameters and the salt explicitely passed as parameters. +-- +-- the function ignore SaltLength from the PSS Parameters +signDigestWithSalt :: HashAlgorithm hash + => ByteString -- ^ Salt to use + -> Maybe Blinder -- ^ optional blinder to use + -> PSSParams hash ByteString ByteString -- ^ PSS Parameters to use + -> PrivateKey -- ^ RSA Private Key + -> ByteString -- ^ Message digest + -> Either Error ByteString +signDigestWithSalt salt blinder params pk mHash + | k < hashLen + saltLen + 2 = Left InvalidParameters + | hashLen /= B.length mHash = Left InvalidParameters + | otherwise = Right $ dp blinder pk em + where k = private_size pk + dbLen = k - hashLen - 1 + saltLen = B.length salt + hashLen = hashDigestSize (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 = B.convert $ hashWith (pssHash params) m' + db = B.concat [B.replicate (dbLen - saltLen - 1) 0,B.singleton 1,salt] + dbmask = (pssMaskGenAlg params) 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 and the salt explicitely passed as parameters. -- -- the function ignore SaltLength from the PSS Parameters @@ -63,22 +94,8 @@ signWithSalt :: HashAlgorithm hash -> 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 +signWithSalt salt blinder params pk m = signDigestWithSalt salt blinder params pk mHash where mHash = B.convert $ hashWith (pssHash params) m - k = private_size pk - dbLen = k - hashLen - 1 - saltLen = B.length salt - hashLen = hashDigestSize (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 = B.convert $ hashWith (pssHash params) m' - db = B.concat [B.replicate (dbLen - saltLen - 1) 0,B.singleton 1,salt] - dbmask = (pssMaskGenAlg params) 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 :: (HashAlgorithm hash, MonadRandom m) @@ -91,6 +108,17 @@ sign blinder params pk m = do salt <- getRandomBytes (pssSaltLength params) return (signWithSalt salt blinder params pk m) +-- | Sign using the PSS Parameters +signDigest :: (HashAlgorithm hash, MonadRandom m) + => Maybe Blinder -- ^ optional blinder to use + -> PSSParams hash ByteString ByteString -- ^ PSS Parameters to use + -> PrivateKey -- ^ RSA Private Key + -> ByteString -- ^ Message digest + -> m (Either Error ByteString) +signDigest blinder params pk digest = do + salt <- getRandomBytes (pssSaltLength params) + return (signDigestWithSalt salt blinder params pk digest) + -- | Sign using the PSS Parameters and an automatically generated blinder. signSafer :: (HashAlgorithm hash, MonadRandom m) => PSSParams hash ByteString ByteString -- ^ PSS Parameters to use @@ -101,6 +129,16 @@ signSafer params pk m = do blinder <- generateBlinder (private_n pk) sign (Just blinder) params pk m +-- | Sign using the PSS Parameters and an automatically generated blinder. +signDigestSafer :: (HashAlgorithm hash, MonadRandom m) + => PSSParams hash ByteString ByteString -- ^ PSS Parameters to use + -> PrivateKey -- ^ private key + -> ByteString -- ^ message digst + -> m (Either Error ByteString) +signDigestSafer params pk digest = do + blinder <- generateBlinder (private_n pk) + signDigest (Just blinder) params pk digest + -- | Verify a signature using the PSS Parameters verify :: HashAlgorithm hash => PSSParams hash ByteString ByteString @@ -110,7 +148,19 @@ verify :: HashAlgorithm hash -> ByteString -- ^ Message to verify -> ByteString -- ^ Signature -> Bool -verify params pk m s +verify params pk m s = verifyDigest params pk mHash s + where mHash = B.convert $ hashWith (pssHash params) m + +-- | Verify a signature using the PSS Parameters +verifyDigest :: HashAlgorithm hash + => PSSParams hash ByteString ByteString + -- ^ PSS Parameters to use to verify, + -- this need to be identical to the parameters when signing + -> PublicKey -- ^ RSA Public Key + -> ByteString -- ^ Digest to verify + -> ByteString -- ^ Signature + -> Bool +verifyDigest params pk mHash s | public_size pk /= B.length s = False | B.last em /= pssTrailerField params = False | not (B.all (== 0) ps0) = False @@ -128,7 +178,6 @@ verify params pk m s db = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor maskedDB dbmask (ps0,z) = B.break (== 1) db (b1,salt) = B.splitAt 1 z - mHash = B.convert $ hashWith (pssHash params) m m' = B.concat [B.replicate 8 0,mHash,salt] h' = hashWith (pssHash params) m' @@ -137,3 +186,4 @@ 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) + From 4270f0027776d643a474cbb98dffe35f83bd2541 Mon Sep 17 00:00:00 2001 From: Baojun Wang Date: Tue, 16 May 2017 10:54:31 -0700 Subject: [PATCH 2/3] Use ``Digest hash`` to represent message digest --- Crypto/PubKey/RSA/PSS.hs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Crypto/PubKey/RSA/PSS.hs b/Crypto/PubKey/RSA/PSS.hs index ab13b4d..71102d6 100644 --- a/Crypto/PubKey/RSA/PSS.hs +++ b/Crypto/PubKey/RSA/PSS.hs @@ -66,13 +66,14 @@ signDigestWithSalt :: HashAlgorithm hash -> Maybe Blinder -- ^ optional blinder to use -> PSSParams hash ByteString ByteString -- ^ PSS Parameters to use -> PrivateKey -- ^ RSA Private Key - -> ByteString -- ^ Message digest + -> Digest hash -- ^ Message digest -> Either Error ByteString -signDigestWithSalt salt blinder params pk mHash +signDigestWithSalt salt blinder params pk digest | k < hashLen + saltLen + 2 = Left InvalidParameters | hashLen /= B.length mHash = Left InvalidParameters | otherwise = Right $ dp blinder pk em where k = private_size pk + mHash = B.convert digest dbLen = k - hashLen - 1 saltLen = B.length salt hashLen = hashDigestSize (pssHash params) @@ -95,7 +96,7 @@ signWithSalt :: HashAlgorithm hash -> ByteString -- ^ Message to sign -> Either Error ByteString signWithSalt salt blinder params pk m = signDigestWithSalt salt blinder params pk mHash - where mHash = B.convert $ hashWith (pssHash params) m + where mHash = hashWith (pssHash params) m -- | Sign using the PSS Parameters sign :: (HashAlgorithm hash, MonadRandom m) @@ -113,7 +114,7 @@ signDigest :: (HashAlgorithm hash, MonadRandom m) => Maybe Blinder -- ^ optional blinder to use -> PSSParams hash ByteString ByteString -- ^ PSS Parameters to use -> PrivateKey -- ^ RSA Private Key - -> ByteString -- ^ Message digest + -> Digest hash -- ^ Message digest -> m (Either Error ByteString) signDigest blinder params pk digest = do salt <- getRandomBytes (pssSaltLength params) @@ -133,7 +134,7 @@ signSafer params pk m = do signDigestSafer :: (HashAlgorithm hash, MonadRandom m) => PSSParams hash ByteString ByteString -- ^ PSS Parameters to use -> PrivateKey -- ^ private key - -> ByteString -- ^ message digst + -> Digest hash -- ^ message digst -> m (Either Error ByteString) signDigestSafer params pk digest = do blinder <- generateBlinder (private_n pk) @@ -149,18 +150,18 @@ verify :: HashAlgorithm hash -> ByteString -- ^ Signature -> Bool verify params pk m s = verifyDigest params pk mHash s - where mHash = B.convert $ hashWith (pssHash params) m + where mHash = hashWith (pssHash params) m -- | Verify a signature using the PSS Parameters verifyDigest :: HashAlgorithm hash => PSSParams hash ByteString ByteString - -- ^ PSS Parameters to use to verify, - -- this need to be identical to the parameters when signing - -> PublicKey -- ^ RSA Public Key - -> ByteString -- ^ Digest to verify - -> ByteString -- ^ Signature + -- ^ PSS Parameters to use to verify, + -- this need to be identical to the parameters when signing + -> PublicKey -- ^ RSA Public Key + -> Digest hash -- ^ Digest to verify + -> ByteString -- ^ Signature -> Bool -verifyDigest params pk mHash s +verifyDigest params pk digest s | public_size pk /= B.length s = False | B.last em /= pssTrailerField params = False | not (B.all (== 0) ps0) = False @@ -168,6 +169,7 @@ verifyDigest params pk mHash s | otherwise = h == B.convert h' where -- parameters hashLen = hashDigestSize (pssHash params) + mHash = B.convert digest dbLen = public_size pk - hashLen - 1 pubBits = public_size pk * 8 -- to change if public_size is converted in bytes -- unmarshall fields From a8902fe119593f826b2b510311efccc85ef20d44 Mon Sep 17 00:00:00 2001 From: Baojun Wang Date: Thu, 18 May 2017 21:00:14 -0700 Subject: [PATCH 3/3] remove redundant condition test on ``hashLen /= B.length mHash`` --- Crypto/PubKey/RSA/PSS.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/Crypto/PubKey/RSA/PSS.hs b/Crypto/PubKey/RSA/PSS.hs index 71102d6..8abd228 100644 --- a/Crypto/PubKey/RSA/PSS.hs +++ b/Crypto/PubKey/RSA/PSS.hs @@ -70,7 +70,6 @@ signDigestWithSalt :: HashAlgorithm hash -> Either Error ByteString signDigestWithSalt salt blinder params pk digest | k < hashLen + saltLen + 2 = Left InvalidParameters - | hashLen /= B.length mHash = Left InvalidParameters | otherwise = Right $ dp blinder pk em where k = private_size pk mHash = B.convert digest