Start with RSA signing! Looks like it works against Haskell RSA test vectors.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -13,5 +13,5 @@ Cargo.lock
|
||||
.DS_Store
|
||||
|
||||
# And some Haskell stuff, because I can't shake it!
|
||||
tests/math/cabal.sandbox.config
|
||||
tests/math/.cabal-sandbox
|
||||
**/cabal.sandbox.config
|
||||
**/.cabal-sandbox
|
||||
|
||||
37
src/rsa/gold_tests.rs
Normal file
37
src/rsa/gold_tests.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use rsa::*;
|
||||
use testing::{make_unsigned,run_test};
|
||||
|
||||
fn get_signing_hash(s: usize) -> &'static SigningHash {
|
||||
match s {
|
||||
0x1 => &SIGNING_HASH_SHA1,
|
||||
0x224 => &SIGNING_HASH_SHA224,
|
||||
0x256 => &SIGNING_HASH_SHA256,
|
||||
0x384 => &SIGNING_HASH_SHA384,
|
||||
0x512 => &SIGNING_HASH_SHA512,
|
||||
_ => panic!("Unacceptable hash")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_signature_tests()
|
||||
{
|
||||
run_test("tests/rsa/signature.test", 7, |case| {
|
||||
let (neg0, dbytes) = case.get("d").unwrap();
|
||||
let (neg1, nbytes) = case.get("n").unwrap();
|
||||
let (neg2, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, kbytes) = case.get("k").unwrap();
|
||||
let (neg3, msg) = case.get("m").unwrap();
|
||||
let (neg4, sig) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0 & !neg1 & !neg2 & !neg3 & !neg4);
|
||||
let hash = get_signing_hash(usize::from(UCN::from_bytes(hbytes)));
|
||||
let size = usize::from(UCN::from_bytes(kbytes));
|
||||
let key = RSAPrivate::new(UCN::from_bytes(nbytes),
|
||||
UCN::from_bytes(dbytes));
|
||||
|
||||
assert!(size % 8 == 0);
|
||||
assert_eq!(key.byte_len * 8, size);
|
||||
let sig2 = key.sign(hash, &msg);
|
||||
assert_eq!(*sig, sig2);
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod core;
|
||||
mod errors;
|
||||
#[cfg(test)]
|
||||
mod gold_tests;
|
||||
mod public;
|
||||
mod private;
|
||||
mod signing_hashes;
|
||||
|
||||
@@ -50,7 +50,7 @@ impl RSAPrivate {
|
||||
let len = n.bits();
|
||||
|
||||
for &(valid_bits, _) in ACCEPTABLE_KEY_SIZES.iter() {
|
||||
if valid_bits > len {
|
||||
if valid_bits >= len {
|
||||
return RSAPrivate {
|
||||
byte_len: valid_bits / 8,
|
||||
n: n.clone(),
|
||||
@@ -61,4 +61,14 @@ impl RSAPrivate {
|
||||
}
|
||||
panic!("Invalid RSA key size in new()")
|
||||
}
|
||||
|
||||
/// Sign a message using the given hash.
|
||||
pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec<u8> {
|
||||
let hash = (sighash.run)(msg);
|
||||
let em = pkcs1_pad(&sighash.ident, &hash, self.byte_len);
|
||||
let m = UCN::from_bytes(&em);
|
||||
let s = sp1(&self.nu, &self.d, &m);
|
||||
let sig = s.to_bytes(self.byte_len);
|
||||
sig
|
||||
}
|
||||
}
|
||||
|
||||
135
tests/rsa/GenerateRSATests.hs
Normal file
135
tests/rsa/GenerateRSATests.hs
Normal file
@@ -0,0 +1,135 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
import Control.Monad
|
||||
import Codec.Crypto.RSA.Pure
|
||||
import Control.Concurrent
|
||||
import Crypto.Random.DRBG
|
||||
import Data.Bits
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
import Data.List
|
||||
import qualified Data.Map.Strict as Map
|
||||
import GHC.Integer.GMP.Internals
|
||||
import Numeric
|
||||
import System.IO
|
||||
import System.ProgressBar
|
||||
import System.Random
|
||||
import Debug.Trace
|
||||
|
||||
numThreads :: Int
|
||||
numThreads = 4
|
||||
|
||||
keySizes :: [Int]
|
||||
keySizes = [512,1024,2048,3072,4096,7680,8192,15360]
|
||||
|
||||
keyIterations :: [Int]
|
||||
keyIterations = replicate 500 512 ++
|
||||
replicate 500 1024 ++
|
||||
replicate 250 2048 ++
|
||||
replicate 125 3072 ++
|
||||
replicate 50 4096 ++
|
||||
replicate 5 7680 ++
|
||||
replicate 2 8192 ++
|
||||
replicate 1 15360
|
||||
|
||||
randomByteString :: CryptoRandomGen g => g -> (BS.ByteString, g)
|
||||
randomByteString g =
|
||||
let Right (bs, g') = genBytes 2 g
|
||||
[h,l] = BS.unpack bs
|
||||
x = (fromIntegral h `shiftL` 8) + (fromIntegral l)
|
||||
Right (res, g'') = genBytes (x `mod` 1024) g'
|
||||
in (res, g'')
|
||||
|
||||
randomHash :: CryptoRandomGen g => g -> ((HashInfo, String), g)
|
||||
randomHash g =
|
||||
randomElement g [(hashSHA1, "1"),
|
||||
(hashSHA224, "224"),
|
||||
(hashSHA256, "256"),
|
||||
(hashSHA384, "384"),
|
||||
(hashSHA512, "512")]
|
||||
|
||||
showBinary :: BS.ByteString -> String
|
||||
showBinary v = go v
|
||||
where
|
||||
go bstr =
|
||||
case BS.uncons bstr of
|
||||
Nothing ->
|
||||
""
|
||||
Just (x, rest) ->
|
||||
let high = showHex (x `shiftR` 4) ""
|
||||
low = showHex (x .&. 0xF) ""
|
||||
in high ++ low ++ go rest
|
||||
|
||||
dump :: Handle -> [(String,String)] -> IO ()
|
||||
dump hndl = mapM_ writeItem
|
||||
where
|
||||
writeItem (name, value) =
|
||||
do hPutStr hndl name
|
||||
hPutStr hndl ": "
|
||||
hPutStrLn hndl value
|
||||
|
||||
mkProgress x y = Progress (fromIntegral x) (fromIntegral y)
|
||||
|
||||
runSignatureGenerator :: Chan Int -> Chan [(String,String)] -> IO ()
|
||||
runSignatureGenerator inputs outputs =
|
||||
do rng0 :: GenBuffered SystemRandom <- newGenIO
|
||||
go Nothing rng0
|
||||
where
|
||||
go Nothing rng0 =
|
||||
do keySize <- readChan inputs
|
||||
go (Just keySize) rng0
|
||||
go (Just keySize) g0 =
|
||||
do unless (keySize `elem` keySizes) $
|
||||
fail ("Bad key size: " ++ show keySize)
|
||||
let Right (public, private, g1) = generateKeyPair g0 keySize
|
||||
unless (private_d private `shiftR` keySize == 0) $
|
||||
fail ("Bad private key size.")
|
||||
unless (public_n public `shiftR` keySize == 0) $
|
||||
fail ("Bad private key size.")
|
||||
let (message, g2) = randomByteString g1
|
||||
let ((hash, hashname), g3) = randomHash g2
|
||||
case rsassa_pkcs1_v1_5_sign hash private (BSL.fromStrict message) of
|
||||
Left _ ->
|
||||
go (Just keySize) g3
|
||||
Right sig ->
|
||||
do writeChan outputs [("d", showHex (private_d private) ""),
|
||||
("n", showHex (public_n public) ""),
|
||||
("h", hashname),
|
||||
("k", showHex keySize ""),
|
||||
("l", showHex (BS.length message) ""),
|
||||
("m", showBinary message),
|
||||
("s", showBinary (BSL.toStrict sig))]
|
||||
go Nothing g3
|
||||
|
||||
writeData :: Chan [(String,String)] -> (Progress -> IO ()) -> Handle -> IO ()
|
||||
writeData outputChan progressBar hndl = go 0
|
||||
where
|
||||
count = fromIntegral (length keyIterations)
|
||||
go x | x == count = return ()
|
||||
| otherwise = do output <- readChan outputChan
|
||||
dump hndl output
|
||||
hFlush hndl
|
||||
progressBar (Progress (x + 1) count)
|
||||
go (x + 1)
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
do sizeChan <- newChan
|
||||
outputChan <- newChan
|
||||
--
|
||||
replicateM_ numThreads $
|
||||
void $ forkIO $ runSignatureGenerator sizeChan outputChan
|
||||
--
|
||||
unless (all (`elem` keySizes) keyIterations) $
|
||||
fail "System setup failure."
|
||||
writeList2Chan sizeChan keyIterations
|
||||
--
|
||||
let bar = autoProgressBar (msg "Generating test material") percentage 60
|
||||
g1 <- withFile "signature.test" WriteMode (writeData outputChan bar)
|
||||
return ()
|
||||
|
||||
randomElement :: CryptoRandomGen g => g -> [a] -> (a, g)
|
||||
randomElement g xs =
|
||||
let Right (bs, g') = genBytes 1 g
|
||||
x = BS.head bs
|
||||
idx = fromIntegral x `mod` length xs
|
||||
in (xs !! idx, g')
|
||||
10031
tests/rsa/signature.test
Normal file
10031
tests/rsa/signature.test
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user