{-# LANGUAGE FlexibleInstances, CPP #-}-- |-- Module : Crypto.Cipher.RSA-- License : BSD-style-- Maintainer : Vincent Hanquez <vincent@snarc.org>-- Stability : experimental-- Portability : Good--moduleCrypto.Cipher.RSA(Error(..),PublicKey(..),PrivateKey(..),HashF,HashASN1,generate,decrypt,encrypt,sign,verify)whereimportControl.Arrow(first)importCrypto.RandomimportData.ByteString(ByteString)importqualifiedData.ByteStringasBimportNumber.ModArithmetic(exponantiation_rtl_binary,inverse)importNumber.Prime(generatePrime)importNumber.SerializeimportData.Maybe(fromJust)dataError=MessageSizeIncorrect-- ^ the message to decrypt is not of the correct size (need to be == private_size)|MessageTooLong-- ^ the message to encrypt is too long (>= private_size - 11)|MessageNotRecognized-- ^ the message decrypted doesn't have a PKCS15 structure (0 2 .. 0 msg)|SignatureTooLong-- ^ the signature generated through the hash is too long to process with this key|RandomGenFailureGenError-- ^ the random generator returns an error. give the opportunity to reseed for example.|KeyInternalError-- ^ the whole key is probably not valid, since the message is bigger than the key sizederiving(Show,Eq)dataPublicKey=PublicKey{public_sz::Int-- ^ size of key in bytes,public_n::Integer-- ^ public p*q,public_e::Integer-- ^ public exponant e}deriving(Show)dataPrivateKey=PrivateKey{private_sz::Int-- ^ size of key in bytes,private_n::Integer-- ^ private p*q,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)typeHashF=ByteString->ByteStringtypeHashASN1=ByteString#if ! (MIN_VERSION_base(4,3,0))instanceMonad(EitherError)wherereturn=Right(Leftx)>>=_=Leftx(Rightx)>>=f=fx#endifpadPKCS1::CryptoRandomGeng=>g->Int->ByteString->EitherError(ByteString,g)padPKCS1rnglenm=do(padding,rng')<-getRandomBytesrng(len-B.lengthm-3)return(B.concat[B.singleton0,B.singleton2,padding,B.singleton0,m],rng')unpadPKCS1::ByteString->EitherErrorByteStringunpadPKCS1packed|signal_error=LeftMessageNotRecognized|otherwise=Rightmwhere(zt,ps0m)=B.splitAt2packed(ps,zm)=B.span(/=0)ps0m(z,m)=B.splitAt1zmsignal_error=(B.unpackzt/=[0,2])||(B.unpackz/=[0])||(B.lengthps<8){- dpSlow computes the decrypted message not using any precomputed cache value.
only n and d need to valid. -}dpSlow::PrivateKey->ByteString->EitherErrorByteStringdpSlowpkc=i2ospOf(private_szpk)$expmod(os2ipc)(private_dpk)(private_npk){- 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::PrivateKey->ByteString->EitherErrorByteStringdpFastpkc=i2ospOf(private_szpk)(m2+h*(private_qpk))whereiC=os2ipcm1=expmodiC(private_dPpk)(private_ppk)m2=expmodiC(private_dQpk)(private_qpk)h=((private_qinvpk)*(m1-m2))`mod`(private_ppk){-| decrypt message using the private key. -}decrypt::PrivateKey->ByteString->EitherErrorByteStringdecryptpkc|B.lengthc/=(private_szpk)=LeftMessageSizeIncorrect|otherwise=dppkc>>=unpadPKCS1wheredp=ifprivate_ppk/=0&&private_qpk/=0thendpFastelsedpSlow{- | encrypt a bytestring using the public key and a CryptoRandomGen random generator.
- the message need to be smaller than the key size - 11
-}encrypt::CryptoRandomGeng=>g->PublicKey->ByteString->EitherError(ByteString,g)encryptrngpkm|B.lengthm>public_szpk-11=LeftMessageTooLong|otherwise=do(em,rng')<-padPKCS1rng(public_szpk)mc<-i2ospOf(public_szpk)$expmod(os2ipem)(public_epk)(public_npk)return(c,rng'){-| sign message using private key, a hash and its ASN1 description -}sign::HashF->HashASN1->PrivateKey->ByteString->EitherErrorByteStringsignhashhashdescpkm=makeSignaturehashhashdesc(private_szpk)m>>=dpkwhered=ifprivate_ppk/=0&&private_qpk/=0thendpFastelsedpSlow{-| verify message with the signed message -}verify::HashF->HashASN1->PublicKey->ByteString->ByteString->EitherErrorBoolverifyhashhashdescpkmsm=dos<-makeSignaturehashhashdesc(public_szpk)mem<-i2ospOf(public_szpk)$expmod(os2ipsm)(public_epk)(public_npk)Right(s==em)-- | generate a pair of (private, public) key of size in bytes.generate::CryptoRandomGeng=>g->Int->Integer->EitherError((PublicKey,PrivateKey),g)generaterngsizee=do((p,q),rng')<-generatePQrngletn=p*qletphi=(p-1)*(q-1)caseinverseephiofNothing->generaterng'sizeeJustd->doletpriv=PrivateKey{private_sz=size,private_n=n,private_d=d,private_p=p,private_q=q,private_dP=d`mod`(p-1),private_dQ=d`mod`(q-1),private_qinv=fromJust$inverseqp-- q and p are coprime, so fromJust is safe.}letpub=PublicKey{public_sz=size,public_n=n,public_e=e}Right((pub,priv),rng')wheregeneratePQg=do(p,g')<-genPrimeg(8*(size`div`2))(q,g'')<-generateQpg'return((p,q),g'')generateQph=do(q,h')<-genPrimeh(8*(size-(size`div`2)))ifp==qthengenerateQph'elsereturn(q,h')genPrimegsz=either(Left.RandomGenFailure)Right$generatePrimegsz{- makeSignature for sign and verify -}makeSignature::HashF->HashASN1->Int->ByteString->EitherErrorByteStringmakeSignaturehashdescrklenm|klen<siglen+1=LeftSignatureTooLong|otherwise=Right$B.concat[B.singleton0,B.singleton1,padding,B.singleton0,signature]wheresignature=descr`B.append`hashmsiglen=B.lengthsignaturepadding=B.replicate(klen-siglen-3)0xff{- get random non-null bytes for encryption padding. -}getRandomBytes::CryptoRandomGeng=>g->Int->EitherError(ByteString,g)getRandomBytesrngn=dogend<-either(Left.RandomGenFailure)Right$genBytesnrnglet(bytes,rng')=first(B.pack.filter(/=0).B.unpack)gendletleft=(n-B.lengthbytes)ifleft==0thenreturn(bytes,rng')elsegetRandomBytesrng'left>>=return.first(B.appendbytes){- convert a positive integer into a bytestring of specific size.
if the number is too big, this will returns an error, otherwise it will pad
the bytestring of 0 -}i2ospOf::Int->Integer->EitherErrorByteStringi2ospOflenm|lenbytes<len=Right$B.replicate(len-lenbytes)0`B.append`bytes|lenbytes==len=Rightbytes|otherwise=LeftKeyInternalErrorwherelenbytes=B.lengthbytesbytes=i2ospmexpmod::Integer->Integer->Integer->Integerexpmod=exponantiation_rtl_binary