{-# LANGUAGE Safe #-}
module Crypto.NewHope.Internal.CPA_KEM where
import Control.DeepSeq
import qualified Data.ByteString as BS
import qualified Crypto.NewHope.CPA_PKE as CPA_PKE
import Crypto.NewHope.FIPS202
import Crypto.NewHope.Internals (N (N1024, N512))
import qualified Crypto.NewHope.Internals as Internals
import Crypto.NewHope.RNG
publicKeyBytes :: Internals.N -> Int
publicKeyBytes = CPA_PKE.publicKeyBytes
secretKeyBytes :: Internals.N -> Int
secretKeyBytes = CPA_PKE.secretKeyBytes
cipherTextBytes :: Internals.N -> Int
cipherTextBytes = CPA_PKE.cipherTextBytes
newtype PublicKey = PublicKey BS.ByteString
instance NFData PublicKey
where
rnf (PublicKey pkData) = seq pkData ()
makePublicKey :: BS.ByteString -> PublicKey
makePublicKey pkData
| not lengthOK = error "Invalid length for PublicKey"
| otherwise = PublicKey pkData
where
len = BS.length pkData
len512 = publicKeyBytes N512
len1024 = publicKeyBytes N1024
lengthOK = len == len512 || len == len1024
getPKData :: PublicKey -> BS.ByteString
getPKData (PublicKey pkData) = pkData
newtype SecretKey = SecretKey BS.ByteString
instance NFData SecretKey
where
rnf (SecretKey skData) = seq skData ()
makeSecretKey :: BS.ByteString -> SecretKey
makeSecretKey skData
| not lengthOK = error "Invalid length for SecretKey"
| otherwise = SecretKey skData
where
len = BS.length skData
len512 = secretKeyBytes N512
len1024 = secretKeyBytes N1024
lengthOK = len == len512 || len == len1024
getSKData :: SecretKey -> BS.ByteString
getSKData (SecretKey skData) = skData
newtype CipherText = CipherText BS.ByteString
instance NFData CipherText
where
rnf (CipherText ctData) = seq ctData ()
makeCipherText :: BS.ByteString -> CipherText
makeCipherText ctData
| not lengthOK = error "Invalid length for CipherText"
| otherwise = CipherText ctData
where
len = BS.length ctData
len512 = cipherTextBytes N512
len1024 = cipherTextBytes N1024
lengthOK = len == len512 || len == len1024
getCTData :: CipherText -> BS.ByteString
getCTData (CipherText ctData) = ctData
newtype SharedSecret = SharedSecret BS.ByteString deriving Eq
instance NFData SharedSecret
where
rnf (SharedSecret ssData) = seq ssData ()
makeSharedSecret :: BS.ByteString -> SharedSecret
makeSharedSecret pkData
| not lengthOK = error "Invalid length for SharedSecret."
| otherwise = SharedSecret pkData
where
goodLength = Internals.sharedSecretBytes
actualLength = BS.length pkData
lengthOK = actualLength == goodLength
getSSData :: SharedSecret -> BS.ByteString
getSSData (SharedSecret ssData) = ssData
keypair :: Context -> Internals.N -> (PublicKey, SecretKey, Context)
keypair ctx n = (pk', sk', ctx')
where
(pk, sk, ctx') = CPA_PKE.keypair ctx n
pk' = makePublicKey $ CPA_PKE.getPKData pk
sk' = makeSecretKey $ CPA_PKE.getSKPolyData sk
encrypt :: Context -> PublicKey -> (CipherText, SharedSecret, Context)
encrypt ctx pk = (ct, ss, ctx')
where
symBytes = fromIntegral Internals.symBytes
(buf, ctx') = randomBytes ctx symBytes
(buf0, buf1) = let buf_2sym = shake256 buf (2 * symBytes)
in BS.splitAt symBytes buf_2sym
ct = let ct' = let pk' = CPA_PKE.makePublicKeyFromBytes $ getPKData pk
in CPA_PKE.encrypt (CPA_PKE.makePlainText buf0) pk' (Internals.makeSeed buf1)
in makeCipherText $ CPA_PKE.getCTData ct'
ss = let ss' = shake256 buf0 symBytes
in makeSharedSecret ss'
decrypt :: CipherText -> SecretKey -> SharedSecret
decrypt ct sk = SharedSecret ss
where
ss = let ss' = let ct' = CPA_PKE.makeCipherTextFromBytes $ getCTData ct
sk' = CPA_PKE.makeSecretKeyFromBytes $ getSKData sk
in CPA_PKE.decrypt ct' sk'
symBytes = fromIntegral Internals.symBytes
in shake256 (CPA_PKE.getPTData ss') symBytes