[英]Use QuickCheck by generating primes
Background 背景
For fun, I'm trying to write a property for quick-check that can test the basic idea behind cryptography with RSA . 为了好玩,我正在尝试编写一个用于快速检查的属性,可以使用RSA测试加密技术背后的基本思想。
p
and q
. p
和q
。 N = p*q
N = p*q
e
is some number relatively prime to (p-1)(q-1)
(in practice, e is usually 3 for fast encoding) e
是(p-1)(q-1)
一些相对素数 (p-1)(q-1)
实际上,对于快速编码,e通常为3) d
is the modular inverse of e
modulo (p-1)(q-1)
d
是e
modulo (p-1)(q-1)
的模逆 For all x
such that 1 < x < N
, it is always true that (x^e)^d = x modulo N
对于所有
x
使得1 < x < N
, (x^e)^d = x modulo N
总是正确的
In other words, x
is the "message", raising it to the e
th power mod N
is the act of "encoding" the message, and raising the encoded message to the d
th power mod N
is the act of "decoding" it. 换句话说,
x
是“消息”,将其提升到e
th power mod N
是“编码”消息的行为,并且将编码消息提升到d
th power mod N
是“解码”它的行为。
(The property is also trivially true for x = 1
, a case which is its own encryption) (对于
x = 1
,该属性也是如此,这是一种自己加密的情况)
Code 码
Here are the methods I have coded up so far: 以下是我到目前为止编写的方法:
import Test.QuickCheck
-- modular exponentiation
modExp :: Integral a => a -> a -> a -> a
modExp y z n = modExp' (y `mod` n) z `mod` n
where modExp' y z | z == 0 = 1
| even z = modExp (y*y) (z `div` 2) n
| odd z = (modExp (y*y) (z `div` 2) n) * y
-- relatively prime
rPrime :: Integral a => a -> a -> Bool
rPrime a b = gcd a b == 1
-- multiplicative inverse (modular)
mInverse :: Integral a => a -> a -> a
mInverse 1 _ = 1
mInverse x y = (n * y + 1) `div` x
where n = x - mInverse (y `mod` x) x
-- just a quick way to test for primality
n `divides` x = x `mod` n == 0
primes = 2:filter isPrime [3..]
isPrime x = null . filter (`divides` x) $ takeWhile (\y -> y*y <= x) primes
-- the property
prop_rsa (p,q,x) = isPrime p &&
isPrime q &&
p /= q &&
x > 1 &&
x < n &&
rPrime e t ==>
x == (x `powModN` e) `powModN` d
where e = 3
n = p*q
t = (p-1)*(q-1)
d = mInverse e t
a `powModN` b = modExp a b n
(Thanks, google and random blog, for the implementation of modular multiplicative inverse ) (谢谢,google和随机博客,用于模块乘法逆的实现 )
Question 题
The problem should be obvious: there are way too many conditions on the property to make it at all usable. 问题应该是显而易见的:财产上有太多条件使其完全可用。 Trying to invoke
quickCheck prop_rsa
in ghci made my terminal hang. 试图在ghci中调用
quickCheck prop_rsa
使我的终端挂起。
So I've poked around the QuickCheck manual a bit, and it says: 所以我在QuickCheck手册上略微探讨了一下,它说:
Properties may take the form
属性可以采取形式
forAll <generator> $ \\<pattern> -> <property>
How do I make a <generator>
for prime numbers? 如何为素数制作
<generator>
? Or with the other constraints, so that quickCheck
doesn't have to sift through a bunch of failed conditions? 或者使用其他约束,以便
quickCheck
不必筛选出一堆失败的条件?
Any other general advice (especially regarding QuickCheck) is welcome. 欢迎任何其他一般性建议(特别是关于QuickCheck)。
Here's one way to make a QuickCheck-compatible prime-number generator (stealing a Sieve of Eratosthenes implementation from http://en.literateprograms.org/Sieve_of_Eratosthenes_(Haskell )): 这是制作与QuickCheck兼容的素数生成器的一种方法(从http://en.literateprograms.org/Sieve_of_Eratosthenes_(Haskell )窃取Eratosthenes实施的Sieve):
import Test.QuickCheck
newtype Prime = Prime Int deriving Show
primes = sieve [2..]
where
sieve (p:xs) = Prime p : sieve [x | x <- xs, x `mod` p > 0]
instance Arbitrary Prime where
arbitrary = do i <- arbitrary
return $ primes!!(abs i)
It can be used in QuickCheck like so: 它可以在QuickCheck中使用,如下所示:
prop_primes_dont_divide (Prime x) (Prime y) = x == y || x `mod` y > 0
For your use, you'd replace p
and q
with (Prime p)
and (Prime q)
in your property. 为了您的使用,您可以在您的财产中用
(Prime p)
和(Prime q)
替换p
和q
。
OK so here's what I did. 好的,这就是我做的。
Top of file 文件顶部
{-# LANGUAGE NoMonomorphismRestriction #-}
import Test.QuickCheck
import Control.Applicative
All code as given in the question, except for prop_rsa. 问题中给出的所有代码,prop_rsa除外。 That was (obviously) heavily modified:
那(显然)经过了大量修改:
prop_rsa = forAll primePair $ \(p,q) ->
let n = p*q
in forAll (genUnder n) $ \x ->
let e = 3
t = (p-1)*(q-1)
d = mInverse e t
a `powModN` b = modExp a b n
in p /= q &&
rPrime e t ==>
x == (x `powModN` e) `powModN` d
The type for primePair
is Gen (Int, Int)
, and the type for genUnder
is Int -> Gen Int
. primePair
的类型是Gen (Int, Int)
, genUnder
的类型是Int -> Gen Int
。 I'm not exactly sure what the magic is behind forAll
but I'm pretty sure this is correct. 我不太确定神奇的是背后究竟
forAll
但我敢肯定,这是正确的。 I've done some ad-hoc adjustments to 1) make sure it fails if I mess up the conditions and 2) make sure the nested forAll
is varying the value of x
across test cases. 我已经做了一些临时调整1)确保它失败如果我弄乱条件和2)确保嵌套的
forAll
在forAll
中改变x
的值。
So here's how to write those generators. 所以这里是如何编写这些生成器。 Once I realized that
<generator>
in the documentation just meant something of type Gen a
, it was cake. 一旦我意识到文档中的
<generator>
只是意味着Gen a
的类型,那就是蛋糕。
genNonzero = (\x -> if x == 0 then 1 else x) `fmap` arbitrary
genUnder :: Int -> Gen Int
genUnder n = ((`mod` n) . abs) `fmap` genNonzero
genSmallPrime = ((\x -> (primes !! (x `mod` 2500))) . abs) `fmap` arbitrary
primePair :: Gen (Int, Int)
primePair = (,) <$> genSmallPrime <*> genSmallPrime
primePair
took some trial and error for me to get right; primePair
采取了一些试验和错误让我做对了; I knew that some combinators like that should work, but I'm still not as familiar with fmap
, <$>
and <*>
as I'd like to be. 我知道像这样的组合器应该可以工作,但我仍然不像我想的那样熟悉
fmap
, <$>
和<*>
。 I restricted the computation to only select from among the first 2500 primes; 我将计算限制为仅从前2500个素数中选择; otherwise it apparently wanted to pick some really big ones that took forever to generate.
否则它显然想要选择一些非常重要的产品。
Random thing to note 随意的事情要注意
Thanks to laziness, d = mInverse et
isn't computed unless the conditions are met. 由于懒惰,除非满足条件,否则不计算
d = mInverse et
。 Which is good, because it's undefined when the condition rPrime et
is false. 哪个好,因为当条件
rPrime et
为假时,它是未定义的。 In English, an integer a
only has a multiplicative inverse (mod b) when a
and b
are relatively prime. 在英语中,当
a
和b
是相对素数时,整数a
仅具有乘法逆(mod b)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.