botan-low 0.0.2.0 → 0.1.0.0
raw patch · 126 files changed
+6889/−7684 lines, 126 filesdep +asyncdep +containersdep +tastydep −cryptondep −tasty-benchdep ~basedep ~botan-bindingsdep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: async, containers, tasty, tasty-hspec, tasty-hunit, tasty-quickcheck
Dependencies removed: crypton, tasty-bench
Dependency ranges changed: base, botan-bindings, bytestring, text
API changes (from Hackage documentation)
- Botan.Low.BlockCipher: [getBlockCipherForeignPtr] :: BlockCipher -> ForeignPtr BotanBlockCipherStruct
- Botan.Low.BlockCipher: type BlockCipher128Name = BlockCipherName
- Botan.Low.Cipher: [getCipherForeignPtr] :: Cipher -> ForeignPtr BotanCipherStruct
- Botan.Low.Cipher: pattern CipherFinal :: CipherUpdateFlags
- Botan.Low.Cipher: pattern CipherUpdate :: CipherUpdateFlags
- Botan.Low.Cipher: pattern Decrypt :: CipherInitFlags
- Botan.Low.Cipher: pattern Encrypt :: CipherInitFlags
- Botan.Low.Cipher: pattern MaskDirection :: CipherInitFlags
- Botan.Low.Cipher: type CipherInitFlags = Word32
- Botan.Low.Cipher: type CipherUpdateFlags = Int
- Botan.Low.Error: catchBotan :: IO a -> (SomeBotanException -> IO a) -> IO a
- Botan.Low.Error: handleBotan :: (SomeBotanException -> IO a) -> IO a -> IO a
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.BadFlagException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.BadMACException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.BadParameterException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.ExceptionThrownException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InsufficientBufferSpaceException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InternalErrorException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InvalidInputException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InvalidKeyLengthException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InvalidObjectException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InvalidObjectStateException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.InvalidVerifierException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.KeyNotSetException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.NotImplementedException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.NullPointerException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.OutOfMemoryException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.SomeBotanException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.StringConversionException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.SystemErrorException
- Botan.Low.Error: instance GHC.Exception.Type.Exception Botan.Low.Error.UnknownException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.BadFlagException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.BadMACException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.BadParameterException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.ExceptionThrownException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InsufficientBufferSpaceException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InternalErrorException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InvalidInputException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InvalidKeyLengthException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InvalidObjectException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InvalidObjectStateException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.InvalidVerifierException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.KeyNotSetException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.NotImplementedException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.NullPointerException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.OutOfMemoryException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.SomeBotanException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.StringConversionException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.SystemErrorException
- Botan.Low.Error: instance GHC.Show.Show Botan.Low.Error.UnknownException
- Botan.Low.Error: pattern BadFlag :: BotanErrorCode
- Botan.Low.Error: pattern BadMAC :: BotanErrorCode
- Botan.Low.Error: pattern BadParameter :: BotanErrorCode
- Botan.Low.Error: pattern ExceptionThrown :: BotanErrorCode
- Botan.Low.Error: pattern HttpError :: BotanErrorCode
- Botan.Low.Error: pattern InsufficientBufferSpace :: BotanErrorCode
- Botan.Low.Error: pattern InternalError :: BotanErrorCode
- Botan.Low.Error: pattern InvalidIdentifier :: BotanErrorCode
- Botan.Low.Error: pattern InvalidInput :: BotanErrorCode
- Botan.Low.Error: pattern InvalidKeyLength :: BotanErrorCode
- Botan.Low.Error: pattern InvalidObject :: BotanErrorCode
- Botan.Low.Error: pattern InvalidObjectState :: BotanErrorCode
- Botan.Low.Error: pattern KeyNotSet :: BotanErrorCode
- Botan.Low.Error: pattern NotImplemented :: BotanErrorCode
- Botan.Low.Error: pattern NullPointer :: BotanErrorCode
- Botan.Low.Error: pattern OutOfMemory :: BotanErrorCode
- Botan.Low.Error: pattern RoughtimeError :: BotanErrorCode
- Botan.Low.Error: pattern StringConversionError :: BotanErrorCode
- Botan.Low.Error: pattern Success :: BotanErrorCode
- Botan.Low.Error: pattern SystemError :: BotanErrorCode
- Botan.Low.Error: pattern TLSError :: BotanErrorCode
- Botan.Low.Error: pattern UnknownError :: BotanErrorCode
- Botan.Low.Error: throwBotanCatchingBool :: HasCallStack => IO BotanErrorCode -> IO Bool
- Botan.Low.Error: throwBotanCatchingInt :: HasCallStack => IO BotanErrorCode -> IO Int
- Botan.Low.Error: throwBotanCatchingSuccess :: HasCallStack => IO BotanErrorCode -> IO Bool
- Botan.Low.Error: throwBotanError :: HasCallStack => BotanErrorCode -> IO a
- Botan.Low.Error: throwBotanErrorWithCallstack :: BotanErrorCode -> CallStack -> IO a
- Botan.Low.Error: throwBotanIfNegative :: HasCallStack => IO BotanErrorCode -> IO BotanErrorCode
- Botan.Low.Error: throwBotanIfNegative_ :: HasCallStack => IO BotanErrorCode -> IO ()
- Botan.Low.Error: tryBotan :: IO a -> IO (Either SomeBotanException a)
- Botan.Low.Error: type BotanErrorCode = CInt
- Botan.Low.FPE: [getFPEForeignPtr] :: FPE -> ForeignPtr BotanFPEStruct
- Botan.Low.FPE: pattern FPEFE1CompatMode :: FPEFlags
- Botan.Low.FPE: pattern FPENone :: FPEFlags
- Botan.Low.FPE: type FPEFlags = Word32
- Botan.Low.HOTP: [getHOTPForeignPtr] :: HOTP -> ForeignPtr BotanHOTPStruct
- Botan.Low.Hash: [getHashForeignPtr] :: Hash -> ForeignPtr BotanHashStruct
- Botan.Low.MAC: [getMACForeignPtr] :: MAC -> ForeignPtr BotanMACStruct
- Botan.Low.MPI: [getMPForeignPtr] :: MP -> ForeignPtr BotanMPStruct
- Botan.Low.PubKey: [getPrivKeyForeignPtr] :: PrivKey -> ForeignPtr BotanPrivKeyStruct
- Botan.Low.PubKey: [getPubKeyForeignPtr] :: PubKey -> ForeignPtr BotanPubKeyStruct
- Botan.Low.PubKey: pattern CheckKeyExpensiveTests :: CheckKeyFlags
- Botan.Low.PubKey: pattern CheckKeyNormalTests :: CheckKeyFlags
- Botan.Low.PubKey: pattern PrivKeyExportDER :: PrivKeyExportFlags
- Botan.Low.PubKey: pattern PrivKeyExportPEM :: PrivKeyExportFlags
- Botan.Low.PubKey: type CheckKeyFlags = Word32
- Botan.Low.PubKey: type PrivKeyExportFlags = Word32
- Botan.Low.PubKey.Decrypt: [getDecryptForeignPtr] :: Decrypt -> ForeignPtr BotanPKOpDecryptStruct
- Botan.Low.PubKey.Encrypt: [getEncryptForeignPtr] :: Encrypt -> ForeignPtr BotanPKOpEncryptStruct
- Botan.Low.PubKey.KeyAgreement: [getKeyAgreementForeignPtr] :: KeyAgreement -> ForeignPtr BotanPKOpKeyAgreementStruct
- Botan.Low.PubKey.KeyEncapsulation: [getKEMDecryptForeignPtr] :: KEMDecrypt -> ForeignPtr BotanPKOpKEMDecryptStruct
- Botan.Low.PubKey.KeyEncapsulation: [getKEMEncryptForeignPtr] :: KEMEncrypt -> ForeignPtr BotanPKOpKEMEncryptStruct
- Botan.Low.PubKey.Sign: [getSignForeignPtr] :: Sign -> ForeignPtr BotanPKOpSignStruct
- Botan.Low.PubKey.Verify: [getVerifyForeignPtr] :: Verify -> ForeignPtr BotanPKOpVerifyStruct
- Botan.Low.RNG: [getRNGForeignPtr] :: RNG -> ForeignPtr BotanRNGStruct
- Botan.Low.SRP6: [getSRP6ServerSessionForeignPtr] :: SRP6ServerSession -> ForeignPtr BotanSRP6ServerSessionStruct
- Botan.Low.TOTP: [getTOTPForeignPtr] :: TOTP -> ForeignPtr BotanTOTPStruct
- Botan.Low.Utility: pattern HexLowerCase :: HexEncodingFlags
- Botan.Low.Utility: pattern HexUpperCase :: HexEncodingFlags
- Botan.Low.Utility: type HexEncodingFlags = Word32
- Botan.Low.X509: [getX509CRLForeignPtr] :: X509CRL -> ForeignPtr BotanX509CRLStruct
- Botan.Low.X509: [getX509CertForeignPtr] :: X509Cert -> ForeignPtr BotanX509CertStruct
+ Botan.Low.BlockCipher: [$sel:getBlockCipherForeignPtr:MkBlockCipher] :: BlockCipher -> ForeignPtr BotanBlockCipherStruct
+ Botan.Low.BlockCipher: cascade :: BlockCipherName -> BlockCipherName -> BlockCipherName
+ Botan.Low.BlockCipher: lion :: HashName -> ByteString -> Maybe Word -> BlockCipherName
+ Botan.Low.BlockCipher: pattern Cascade :: BlockCipherName
+ Botan.Low.BlockCipher: pattern Lion :: BlockCipherName
+ Botan.Low.Cipher: CipherDecrypt :: CipherInitFlags
+ Botan.Low.Cipher: CipherEncrypt :: CipherInitFlags
+ Botan.Low.Cipher: CipherFinal :: CipherUpdateFlags
+ Botan.Low.Cipher: CipherMaskDirection :: CipherInitFlags
+ Botan.Low.Cipher: CipherUpdate :: CipherUpdateFlags
+ Botan.Low.Cipher: [$sel:getCipherForeignPtr:MkCipher] :: Cipher -> ForeignPtr BotanCipherStruct
+ Botan.Low.Cipher: cipherInitFlags :: CipherInitFlags -> Word32
+ Botan.Low.Cipher: cipherUpdateFlags :: CipherUpdateFlags -> Word32
+ Botan.Low.Cipher: data CipherInitFlags
+ Botan.Low.Cipher: data CipherUpdateFlags
+ Botan.Low.Cipher: instance GHC.Classes.Eq Botan.Low.Cipher.CipherInitFlags
+ Botan.Low.Cipher: instance GHC.Classes.Eq Botan.Low.Cipher.CipherUpdateFlags
+ Botan.Low.Cipher: instance GHC.Show.Show Botan.Low.Cipher.CipherInitFlags
+ Botan.Low.Cipher: instance GHC.Show.Show Botan.Low.Cipher.CipherUpdateFlags
+ Botan.Low.Error: BotanErrorMessage :: ByteString -> BotanErrorMessage
+ Botan.Low.Error: data BotanErrorCode
+ Botan.Low.Error: newtype BotanErrorMessage
+ Botan.Low.FPE: FPEFE1CompatMode :: FPEFlags
+ Botan.Low.FPE: FPENone :: FPEFlags
+ Botan.Low.FPE: [$sel:getFPEForeignPtr:MkFPE] :: FPE -> ForeignPtr BotanFPEStruct
+ Botan.Low.FPE: data FPEFlags
+ Botan.Low.FPE: fPEFlags :: FPEFlags -> Word32
+ Botan.Low.HOTP: [$sel:getHOTPForeignPtr:MkHOTP] :: HOTP -> ForeignPtr BotanHOTPStruct
+ Botan.Low.Hash: [$sel:getHashForeignPtr:MkHash] :: Hash -> ForeignPtr BotanHashStruct
+ Botan.Low.MAC: [$sel:getMACForeignPtr:MkMAC] :: MAC -> ForeignPtr BotanMACStruct
+ Botan.Low.MPI: [$sel:getMPForeignPtr:MkMP] :: MP -> ForeignPtr BotanMPStruct
+ Botan.Low.PubKey: CheckKeyExpensiveTests :: CheckKeyFlags
+ Botan.Low.PubKey: CheckKeyNormalTests :: CheckKeyFlags
+ Botan.Low.PubKey: PrivKeyExportDER :: PrivKeyExportFlags
+ Botan.Low.PubKey: PrivKeyExportPEM :: PrivKeyExportFlags
+ Botan.Low.PubKey: [$sel:getPrivKeyForeignPtr:MkPrivKey] :: PrivKey -> ForeignPtr BotanPrivKeyStruct
+ Botan.Low.PubKey: [$sel:getPubKeyForeignPtr:MkPubKey] :: PubKey -> ForeignPtr BotanPubKeyStruct
+ Botan.Low.PubKey: checkKeyFlags :: CheckKeyFlags -> Word32
+ Botan.Low.PubKey: data CheckKeyFlags
+ Botan.Low.PubKey: data PrivKeyExportFlags
+ Botan.Low.PubKey: privKeyExportFlags :: PrivKeyExportFlags -> Word32
+ Botan.Low.PubKey.Decrypt: [$sel:getDecryptForeignPtr:MkDecrypt] :: Decrypt -> ForeignPtr BotanPKOpDecryptStruct
+ Botan.Low.PubKey.Encrypt: [$sel:getEncryptForeignPtr:MkEncrypt] :: Encrypt -> ForeignPtr BotanPKOpEncryptStruct
+ Botan.Low.PubKey.KeyAgreement: [$sel:getKeyAgreementForeignPtr:MkKeyAgreement] :: KeyAgreement -> ForeignPtr BotanPKOpKeyAgreementStruct
+ Botan.Low.PubKey.KeyEncapsulation: [$sel:getKEMDecryptForeignPtr:MkKEMDecrypt] :: KEMDecrypt -> ForeignPtr BotanPKOpKEMDecryptStruct
+ Botan.Low.PubKey.KeyEncapsulation: [$sel:getKEMEncryptForeignPtr:MkKEMEncrypt] :: KEMEncrypt -> ForeignPtr BotanPKOpKEMEncryptStruct
+ Botan.Low.PubKey.Sign: [$sel:getSignForeignPtr:MkSign] :: Sign -> ForeignPtr BotanPKOpSignStruct
+ Botan.Low.PubKey.Verify: [$sel:getVerifyForeignPtr:MkVerify] :: Verify -> ForeignPtr BotanPKOpVerifyStruct
+ Botan.Low.RNG: [$sel:getRNGForeignPtr:MkRNG] :: RNG -> ForeignPtr BotanRNGStruct
+ Botan.Low.SRP6: [$sel:getSRP6ServerSessionForeignPtr:MkSRP6ServerSession] :: SRP6ServerSession -> ForeignPtr BotanSRP6ServerSessionStruct
+ Botan.Low.TOTP: [$sel:getTOTPForeignPtr:MkTOTP] :: TOTP -> ForeignPtr BotanTOTPStruct
+ Botan.Low.Utility: HexLowerCase :: HexEncodingFlags
+ Botan.Low.Utility: HexUpperCase :: HexEncodingFlags
+ Botan.Low.Utility: data HexEncodingFlags
+ Botan.Low.X509: [$sel:getX509CRLForeignPtr:MkX509CRL] :: X509CRL -> ForeignPtr BotanX509CRLStruct
+ Botan.Low.X509: [$sel:getX509CertForeignPtr:MkX509Cert] :: X509Cert -> ForeignPtr BotanX509CertStruct
- Botan.Low.BlockCipher: blockCipher128s :: [BlockCipher128Name]
+ Botan.Low.BlockCipher: blockCipher128s :: [BlockCipherName]
- Botan.Low.BlockCipher: pattern AES128 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern AES128 :: BlockCipherName
- Botan.Low.BlockCipher: pattern AES192 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern AES192 :: BlockCipherName
- Botan.Low.BlockCipher: pattern AES256 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern AES256 :: BlockCipherName
- Botan.Low.BlockCipher: pattern ARIA128 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern ARIA128 :: BlockCipherName
- Botan.Low.BlockCipher: pattern ARIA192 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern ARIA192 :: BlockCipherName
- Botan.Low.BlockCipher: pattern ARIA256 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern ARIA256 :: BlockCipherName
- Botan.Low.BlockCipher: pattern Camellia128 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Camellia128 :: BlockCipherName
- Botan.Low.BlockCipher: pattern Camellia192 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Camellia192 :: BlockCipherName
- Botan.Low.BlockCipher: pattern Camellia256 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Camellia256 :: BlockCipherName
- Botan.Low.BlockCipher: pattern Noekeon :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Noekeon :: BlockCipherName
- Botan.Low.BlockCipher: pattern SEED :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern SEED :: BlockCipherName
- Botan.Low.BlockCipher: pattern SM4 :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern SM4 :: BlockCipherName
- Botan.Low.BlockCipher: pattern Serpent :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Serpent :: BlockCipherName
- Botan.Low.BlockCipher: pattern Twofish :: BlockCipher128Name
+ Botan.Low.BlockCipher: pattern Twofish :: BlockCipherName
- Botan.Low.Cipher: ccmMode :: BlockCipher128Name -> AEADName
+ Botan.Low.Cipher: ccmMode :: BlockCipherName -> AEADName
- Botan.Low.Cipher: ccmModeWith :: BlockCipher128Name -> Int -> Int -> AEADName
+ Botan.Low.Cipher: ccmModeWith :: BlockCipherName -> Int -> Int -> AEADName
- Botan.Low.Cipher: gcmMode :: BlockCipher128Name -> AEADName
+ Botan.Low.Cipher: gcmMode :: BlockCipherName -> AEADName
- Botan.Low.Cipher: gcmModeWith :: BlockCipher128Name -> Int -> AEADName
+ Botan.Low.Cipher: gcmModeWith :: BlockCipherName -> Int -> AEADName
- Botan.Low.Cipher: ocbMode :: BlockCipher128Name -> AEADName
+ Botan.Low.Cipher: ocbMode :: BlockCipherName -> AEADName
- Botan.Low.Cipher: ocbModeWith :: BlockCipher128Name -> Int -> AEADName
+ Botan.Low.Cipher: ocbModeWith :: BlockCipherName -> Int -> AEADName
- Botan.Low.Cipher: sivMode :: BlockCipher128Name -> AEADName
+ Botan.Low.Cipher: sivMode :: BlockCipherName -> AEADName
- Botan.Low.Error: BadFlagException :: BotanErrorCode -> ErrorMessage -> CallStack -> BadFlagException
+ Botan.Low.Error: BadFlagException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> BadFlagException
- Botan.Low.Error: BadMACException :: BotanErrorCode -> ErrorMessage -> CallStack -> BadMACException
+ Botan.Low.Error: BadMACException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> BadMACException
- Botan.Low.Error: BadParameterException :: BotanErrorCode -> ErrorMessage -> CallStack -> BadParameterException
+ Botan.Low.Error: BadParameterException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> BadParameterException
- Botan.Low.Error: ExceptionThrownException :: BotanErrorCode -> ErrorMessage -> CallStack -> ExceptionThrownException
+ Botan.Low.Error: ExceptionThrownException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> ExceptionThrownException
- Botan.Low.Error: InsufficientBufferSpaceException :: BotanErrorCode -> ErrorMessage -> CallStack -> InsufficientBufferSpaceException
+ Botan.Low.Error: InsufficientBufferSpaceException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InsufficientBufferSpaceException
- Botan.Low.Error: InternalErrorException :: BotanErrorCode -> ErrorMessage -> CallStack -> InternalErrorException
+ Botan.Low.Error: InternalErrorException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InternalErrorException
- Botan.Low.Error: InvalidInputException :: BotanErrorCode -> ErrorMessage -> CallStack -> InvalidInputException
+ Botan.Low.Error: InvalidInputException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InvalidInputException
- Botan.Low.Error: InvalidKeyLengthException :: BotanErrorCode -> ErrorMessage -> CallStack -> InvalidKeyLengthException
+ Botan.Low.Error: InvalidKeyLengthException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InvalidKeyLengthException
- Botan.Low.Error: InvalidObjectException :: BotanErrorCode -> ErrorMessage -> CallStack -> InvalidObjectException
+ Botan.Low.Error: InvalidObjectException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InvalidObjectException
- Botan.Low.Error: InvalidObjectStateException :: BotanErrorCode -> ErrorMessage -> CallStack -> InvalidObjectStateException
+ Botan.Low.Error: InvalidObjectStateException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> InvalidObjectStateException
- Botan.Low.Error: KeyNotSetException :: BotanErrorCode -> ErrorMessage -> CallStack -> KeyNotSetException
+ Botan.Low.Error: KeyNotSetException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> KeyNotSetException
- Botan.Low.Error: NotImplementedException :: BotanErrorCode -> ErrorMessage -> CallStack -> NotImplementedException
+ Botan.Low.Error: NotImplementedException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> NotImplementedException
- Botan.Low.Error: NullPointerException :: BotanErrorCode -> ErrorMessage -> CallStack -> NullPointerException
+ Botan.Low.Error: NullPointerException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> NullPointerException
- Botan.Low.Error: OutOfMemoryException :: BotanErrorCode -> ErrorMessage -> CallStack -> OutOfMemoryException
+ Botan.Low.Error: OutOfMemoryException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> OutOfMemoryException
- Botan.Low.Error: StringConversionException :: BotanErrorCode -> ErrorMessage -> CallStack -> StringConversionException
+ Botan.Low.Error: StringConversionException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> StringConversionException
- Botan.Low.Error: SystemErrorException :: BotanErrorCode -> ErrorMessage -> CallStack -> SystemErrorException
+ Botan.Low.Error: SystemErrorException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> SystemErrorException
- Botan.Low.Error: UnknownException :: BotanErrorCode -> ErrorMessage -> CallStack -> UnknownException
+ Botan.Low.Error: UnknownException :: BotanErrorCode -> BotanErrorMessage -> CallStack -> UnknownException
- Botan.Low.Error: botanErrorLastExceptionMessage :: IO ErrorMessage
+ Botan.Low.Error: botanErrorLastExceptionMessage :: IO BotanErrorMessage
- Botan.Low.PubKey: mkPrivKeyLoad1_name :: (Ptr BotanPrivKey -> BotanMP -> ConstPtr CChar -> IO BotanErrorCode) -> MP -> ByteString -> IO PrivKey
+ Botan.Low.PubKey: mkPrivKeyLoad1_name :: (Ptr BotanPrivKey -> BotanMP -> ConstPtr CChar -> IO CInt) -> MP -> ByteString -> IO PrivKey
- Botan.Low.PubKey: mkPrivKeyLoad3 :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode) -> MP -> MP -> MP -> IO PrivKey
+ Botan.Low.PubKey: mkPrivKeyLoad3 :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> IO PrivKey
- Botan.Low.PubKey: mkPrivKeyLoad4 :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode) -> MP -> MP -> MP -> MP -> IO PrivKey
+ Botan.Low.PubKey: mkPrivKeyLoad4 :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> MP -> IO PrivKey
- Botan.Low.PubKey: mkPubKeyLoad2 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> IO BotanErrorCode) -> MP -> MP -> IO PubKey
+ Botan.Low.PubKey: mkPubKeyLoad2 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> IO PubKey
- Botan.Low.PubKey: mkPubKeyLoad2_name :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> ConstPtr CChar -> IO BotanErrorCode) -> MP -> MP -> ByteString -> IO PubKey
+ Botan.Low.PubKey: mkPubKeyLoad2_name :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> ConstPtr CChar -> IO CInt) -> MP -> MP -> ByteString -> IO PubKey
- Botan.Low.PubKey: mkPubKeyLoad3 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode) -> MP -> MP -> MP -> IO PubKey
+ Botan.Low.PubKey: mkPubKeyLoad3 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> IO PubKey
- Botan.Low.PubKey: mkPubKeyLoad4 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode) -> MP -> MP -> MP -> MP -> IO PubKey
+ Botan.Low.PubKey: mkPubKeyLoad4 :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> MP -> IO PubKey
- Botan.Low.PwdHash: pbkdf2 :: MACName -> PBKDFName
+ Botan.Low.PwdHash: pbkdf2 :: HashName -> PBKDFName
- Botan.Low.SRP6: srp6ServerSessionStep2 :: SRP6ServerSession -> SRP6AValue -> IO SRP6SharedSecret
+ Botan.Low.SRP6: srp6ServerSessionStep2 :: SRP6ServerSession -> SRP6AValue -> DLGroupName -> IO SRP6SharedSecret
Files
- CHANGELOG.md +97/−0
- README.md +8/−3
- bench/Bcrypt.hs +0/−74
- botan-low.cabal +112/−828
- src/Botan/Low/Bcrypt.hs +34/−34
- src/Botan/Low/BlockCipher.hs +412/−255
- src/Botan/Low/Cipher.hs +173/−159
- src/Botan/Low/Error.hs +33/−394
- src/Botan/Low/Error/Internal.hs +282/−0
- src/Botan/Low/FPE.hs +31/−40
- src/Botan/Low/HOTP.hs +52/−51
- src/Botan/Low/Hash.hs +81/−85
- src/Botan/Low/Internal/ByteString.hs +107/−0
- src/Botan/Low/Internal/String.hs +16/−0
- src/Botan/Low/KDF.hs +55/−44
- src/Botan/Low/KeyWrap.hs +10/−13
- src/Botan/Low/MAC.hs +49/−52
- src/Botan/Low/MPI.hs +66/−62
- src/Botan/Low/Make.hs +119/−67
- src/Botan/Low/Prelude.hs +0/−247
- src/Botan/Low/PubKey.hs +240/−246
- src/Botan/Low/PubKey/DH.hs +7/−10
- src/Botan/Low/PubKey/DSA.hs +9/−12
- src/Botan/Low/PubKey/Decrypt.hs +41/−35
- src/Botan/Low/PubKey/ECDH.hs +8/−7
- src/Botan/Low/PubKey/ECDSA.hs +8/−7
- src/Botan/Low/PubKey/Ed25519.hs +12/−11
- src/Botan/Low/PubKey/ElGamal.hs +9/−12
- src/Botan/Low/PubKey/Encrypt.hs +50/−49
- src/Botan/Low/PubKey/KeyAgreement.hs +77/−92
- src/Botan/Low/PubKey/KeyEncapsulation.hs +45/−50
- src/Botan/Low/PubKey/RSA.hs +12/−10
- src/Botan/Low/PubKey/SM2.hs +8/−7
- src/Botan/Low/PubKey/Sign.hs +32/−34
- src/Botan/Low/PubKey/Verify.hs +34/−41
- src/Botan/Low/PubKey/X25519.hs +12/−13
- src/Botan/Low/PwdHash.hs +196/−101
- src/Botan/Low/RNG.hs +32/−37
- src/Botan/Low/Remake.hs +31/−130
- src/Botan/Low/SRP6.hs +420/−259
- src/Botan/Low/TOTP.hs +52/−51
- src/Botan/Low/Utility.hs +94/−106
- src/Botan/Low/Version.hs +30/−36
- src/Botan/Low/View.hs +16/−22
- src/Botan/Low/X509.hs +161/−160
- src/Botan/Low/X509/CA.hs +0/−115
- src/Botan/Low/X509/CRL.hs +0/−151
- src/Botan/Low/X509/CSR.hs +0/−85
- src/Botan/Low/X509/DN.hs +0/−60
- src/Botan/Low/X509/Extensions.hs +0/−26
- src/Botan/Low/X509/Options.hs +0/−157
- src/Botan/Low/X509/Path.hs +0/−141
- src/Botan/Low/X509/Store.hs +0/−326
- src/Botan/Low/ZFEC.hs +23/−21
- test/Botan/Low/BcryptSpec.hs +0/−25
- test/Botan/Low/BlockCipherSpec.hs +0/−68
- test/Botan/Low/CipherSpec.hs +0/−223
- test/Botan/Low/FPESpec.hs +0/−97
- test/Botan/Low/HOTPSpec.hs +0/−27
- test/Botan/Low/HashSpec.hs +0/−48
- test/Botan/Low/KDFSpec.hs +0/−34
- test/Botan/Low/KeyWrapSpec.hs +0/−42
- test/Botan/Low/MACSpec.hs +0/−132
- test/Botan/Low/MPISpec.hs +0/−382
- test/Botan/Low/PubKey/DHSpec.hs +0/−69
- test/Botan/Low/PubKey/DSASpec.hs +0/−76
- test/Botan/Low/PubKey/DecryptSpec.hs +0/−43
- test/Botan/Low/PubKey/ECDHSpec.hs +0/−78
- test/Botan/Low/PubKey/ECDSASpec.hs +0/−72
- test/Botan/Low/PubKey/Ed25519Spec.hs +0/−34
- test/Botan/Low/PubKey/ElGamalSpec.hs +0/−71
- test/Botan/Low/PubKey/EncryptSpec.hs +0/−42
- test/Botan/Low/PubKey/KeyAgreementSpec.hs +0/−50
- test/Botan/Low/PubKey/KeyEncapsulationSpec.hs +0/−67
- test/Botan/Low/PubKey/RSASpec.hs +0/−43
- test/Botan/Low/PubKey/SM2Spec.hs +0/−71
- test/Botan/Low/PubKey/SignSpec.hs +0/−67
- test/Botan/Low/PubKey/VerifySpec.hs +0/−79
- test/Botan/Low/PubKey/X25519Spec.hs +0/−34
- test/Botan/Low/PubKeySpec.hs +0/−188
- test/Botan/Low/PwdHashSpec.hs +0/−61
- test/Botan/Low/RNGSpec.hs +0/−42
- test/Botan/Low/SRP6Spec.hs +0/−71
- test/Botan/Low/TOTPSpec.hs +0/−31
- test/Botan/Low/UtilitySpec.hs +0/−41
- test/Botan/Low/X509Spec.hs +0/−171
- test/Botan/Low/ZFECSpec.hs +0/−27
- test/Main.hs +117/−0
- test/Test/Botan/Low/Bcrypt.hs +38/−0
- test/Test/Botan/Low/BlockCipher.hs +99/−0
- test/Test/Botan/Low/Cipher.hs +244/−0
- test/Test/Botan/Low/FPE.hs +107/−0
- test/Test/Botan/Low/HOTP.hs +39/−0
- test/Test/Botan/Low/Hash.hs +59/−0
- test/Test/Botan/Low/KDF.hs +44/−0
- test/Test/Botan/Low/KeyWrap.hs +52/−0
- test/Test/Botan/Low/MAC.hs +141/−0
- test/Test/Botan/Low/MPI.hs +394/−0
- test/Test/Botan/Low/PubKey.hs +211/−0
- test/Test/Botan/Low/PubKey/DH.hs +82/−0
- test/Test/Botan/Low/PubKey/DSA.hs +70/−0
- test/Test/Botan/Low/PubKey/Decrypt.hs +57/−0
- test/Test/Botan/Low/PubKey/ECDH.hs +91/−0
- test/Test/Botan/Low/PubKey/ECDSA.hs +85/−0
- test/Test/Botan/Low/PubKey/Ed25519.hs +45/−0
- test/Test/Botan/Low/PubKey/ElGamal.hs +78/−0
- test/Test/Botan/Low/PubKey/Encrypt.hs +53/−0
- test/Test/Botan/Low/PubKey/KeyAgreement.hs +63/−0
- test/Test/Botan/Low/PubKey/KeyEncapsulation.hs +82/−0
- test/Test/Botan/Low/PubKey/RSA.hs +57/−0
- test/Test/Botan/Low/PubKey/SM2.hs +84/−0
- test/Test/Botan/Low/PubKey/Sign.hs +81/−0
- test/Test/Botan/Low/PubKey/Verify.hs +91/−0
- test/Test/Botan/Low/PubKey/X25519.hs +45/−0
- test/Test/Botan/Low/PwdHash.hs +169/−0
- test/Test/Botan/Low/RNG.hs +59/−0
- test/Test/Botan/Low/SRP6.hs +75/−0
- test/Test/Botan/Low/SRP6/Example.hs +202/−0
- test/Test/Botan/Low/TOTP.hs +43/−0
- test/Test/Botan/Low/Utility.hs +55/−0
- test/Test/Botan/Low/Version.hs +20/−0
- test/Test/Botan/Low/X509.hs +186/−0
- test/Test/Botan/Low/ZFEC.hs +38/−0
- test/Test/Prelude.hs +0/−50
- test/Test/Util/ByteString.hs +19/−0
- test/Test/Util/Hspec.hs +18/−0
CHANGELOG.md view
@@ -1,5 +1,102 @@ # Changelog +## 0.1.0.0 -- 2025-12-30++* BREAKING: remove experimental FFI code related to `x509`. See PR+ [#49](https://github.com/haskell-cryptography/botan/pull/49). This code might+ be restored in some form in the future, but as of now it does not compile any+ more, so it is removed. The modules that are removed are:+ - `Botan.Low.X509.CA`+ - `Botan.Low.X509.CRL`+ - `Botan.Low.X509.CSR`+ - `Botan.Low.X509.DN`+ - `Botan.Low.X509.Extensions`+ - `Botan.Low.X509.Options`+ - `Botan.Low.X509.Path`+ - `Botan.Low.X509.Store`+* BREAKING: remove the `XFFI` cabal flag. See PR+ [#49](https://github.com/haskell-cryptography/botan/pull/49). It has no effect+ now that the experimental `x509` code is moved.+* PATCH: enable `-Wall` in addition to a number of other GHC warnings. See PR+ [#52](https://github.com/haskell-cryptography/botan/pull/52).+* PATCH: use `GHC2021` as the default language. See PR+ [#52](https://github.com/haskell-cryptography/botan/pull/52).+* PATCH: update documentation in the `Botan.Low.Error` module. See PR+ [#62](https://github.com/haskell-cryptography/botan/pull/62).+* PATCH: update documentation in the `Botan.Low.Utility` module. See PR+ [#62](https://github.com/haskell-cryptography/botan/pull/62).+* PATCH: update documentation in the `Botan.Low.Version` module. See PR+ [#62](https://github.com/haskell-cryptography/botan/pull/62).+* PATCH: update documentation in the `Botan.Low.View` module. See PR+ [#62](https://github.com/haskell-cryptography/botan/pull/62).+* BREAKING: `Botan.Low.PwdHash.pbkdf2` now takes a `HashName` instead of a+ `MacName`. See PR+ [#63](https://github.com/haskell-cryptography/botan/pull/63).+* PATCH: update documentation in the `Botan.Low.PwdHash` module. See PR+ [#63](https://github.com/haskell-cryptography/botan/pull/63).+* PATCH: introduce a work-around for a bug found in+ `Botan.Low.SRP6.srp6ServerSessionStep1`, which would always throw an+ exception. See PR+ [#65](https://github.com/haskell-cryptography/botan/pull/65).+* BREAKING: add a `DLGroupName` function argument to+ `Botan.Low.SRP6.srp6ServerSessionStep2`. See PR+ [#65](https://github.com/haskell-cryptography/botan/pull/65).+* PATCH: update documentation in the `Botan.Low.SRP6` module. See PR+ [#65](https://github.com/haskell-cryptography/botan/pull/65).+* PATCH: update documentation in the `Botan.Low.BlockCipher` module. See PR+ [#74](https://github.com/haskell-cryptography/botan/pull/74).+* BREAKING: remove the `BlockCipher128Name` type synonym from+ `Botan.Low.BlockCipher`. See PR+ [#74](https://github.com/haskell-cryptography/botan/pull/74).+* NON-BREAKING: in the `BlockCipher` module, add new pattern synonyms `Lion` and+ `Cascade` and accompanying utility functions `lion` and `cascade` for the+ "Lion" and "Cascade" ciphers respectively. See PR+ [#74](https://github.com/haskell-cryptography/botan/pull/74).+* PATCH: fix an "address out of bounds" bug in `blockCipherEncryptBlocks` and+ `blockCipherDecryptBlocks` that occasionally caused segfaults. See PR+ [#74](https://github.com/haskell-cryptography/botan/pull/74).+* PATCH: fix an "insufficient buffer space" bug in+ `Botan.Low.PubKey.Encrypt.encrypt` and `Botan.Low.PubKey.Decrypt.decrypt`. See+ PR [#79](https://github.com/haskell-cryptography/botan/pull/79), PR+ [#87](https://github.com/haskell-cryptography/botan/pull/87).+* PATCH: Fix an "insufficient buffer space" bug in+ `Botan.Low.Cipher.cipherUpdate`. See PR+ [#84](https://github.com/haskell-cryptography/botan/pull/84).+* BREAKING: rework errors. See PR+ [#94](https://github.com/haskell-cryptography/botan/pull/94).+ - Change `BotanErrorCode` from a type synonym to a newtype around `CInt`.+ - Add a new `BotanErrorMessage` newtype around `ByteString`, and use it in+ `botanErrorLastExceptionMessage`.+ - Remove pattern synonyms for `BotanErrorCode`. Use `BOTAN_FFI_ERROR` from+ `botan-bindings` instead.+ - Hide functions related to error number handling in the internals of the+ library. This includes all function names starting with `throwBotan`.+ - Remove `tryBotan`, `catchBotan`, and `handleBotan`.+* BREAKING: change flags from numbers into datatypes. See PR+ [#95](https://github.com/haskell-cryptography/botan/pull/95) The flag+ datatypes come with new helper functions to convert the datatype into a+ number. For example, `CipherInitFlags` gets a `cipherInitFlags` function. The+ changed flag types are:+ - `CipherInitFlags`+ - `CipherUpdateFlags`+ - `FPEFlags`+ - `CheckKeyFlags`+ - `PrivKeyExportFlags`+ - `HexEncodingFlags`+* BREAKING: remove the `botan-low-bench` benchmark. See PR+ [#98](https://github.com/haskell-cryptography/botan/pull/98).+* PATCH: fix a bug where some functions returning a `Bool` were returning+ `False` instead of throwing an exception and vice versa. See PR+ [#105](https://github.com/haskell-cryptography/botan/pull/105). These+ functions are:+ - `pubKeyCheckKey`+ - `x509CertHostnameMatch`+ - `x509IsRevoked`+* BREAKING: enable `NoFieldSelectors`. See PR+ [#106](https://github.com/haskell-cryptography/botan/pull/106). As a result,+ this package no longer exports field selectors for datatypes defined in the+ package.+ ## 0.0.2.0 -- 2025-09-17 * PATCH: update the changelog to change the release date of version 0.0.1.0 from
README.md view
@@ -1,7 +1,12 @@ # Welcome to botan-low -Low-level bindings to the [Botan](https://botan.randombit.net/) cryptography library.+Low-level bindings to the [Botan](https://botan.randombit.net/) cryptography+library. -> Botan's goal is to be the best option for cryptography in C++ by offering the tools necessary to implement a range of practical systems, such as TLS protocol, X.509 certificates, modern AEAD ciphers, PKCS#11 and TPM hardware support, password hashing, and post quantum crypto schemes.+> Botan's goal is to be the best option for cryptography in C++ by offering the+> tools necessary to implement a range of practical systems, such as TLS+> protocol, X.509 certificates, modern AEAD ciphers, PKCS#11 and TPM hardware+> support, password hashing, and post quantum crypto schemes. -For more information, see the [README on Github](https://github.com/haskellfoundation/botan)+For more information, see the [README on+Github](https://github.com/haskell-cryptography/botan).
− bench/Bcrypt.hs
@@ -1,74 +0,0 @@-import Test.Tasty.Bench--import Prelude--import Data.ByteString (ByteString(..))-import qualified Data.ByteString as ByteString--import qualified Botan.Bindings.RNG as Botan-import qualified Botan.Bindings.Bcrypt as Botan-import qualified Botan.Low.RNG as Botan-import qualified Botan.Low.Bcrypt as Botan-import qualified Botan.Low.Hash as Botan---import qualified Crypto.KDF.BCrypt as Crypton-import qualified Crypto.Hash as Crypton-import qualified Crypto.Hash.Algorithms as Crypton--password :: ByteString-password = "Fee fi fo fum!"--cryptonBcrypt :: Int -> IO ByteString-cryptonBcrypt factor = Crypton.hashPassword factor password--botanBcrypt :: Int -> IO ByteString-botanBcrypt factor = do- rng <- Botan.rngInit "system"- Botan.bcryptGenerate password rng factor---plaintext :: ByteString-plaintext = "Fee fi fo fum! I smell the blood of an Englishman! Be he alive or be he dead, I'll grind his bones to make my bread!"--longtext :: ByteString-longtext = ByteString.concat $ replicate 10000 $ plaintext--cryptonHash :: ByteString -> Crypton.Digest Crypton.SHA3_512-cryptonHash = Crypton.hash---- TODO: hashWithName is not exposed from Botan.Low.Hash. See issue #34.-{- botanHash :: ByteString -> IO Botan.HashDigest- botanHash = Botan.hashWithName "SHA-3(512)"--}--main :: IO ()-main = defaultMain- [ bgroup "Bcrypt work factor"- [ bgroup "Crypton"- [ bench "twelve" $ nfIO $ cryptonBcrypt 12- , bench "fourteen" $ nfIO $ cryptonBcrypt 14- , bench "sixteen" $ nfIO $ cryptonBcrypt 16- ]- , bgroup "Botan"- [ bench "twelve" $ nfIO $ botanBcrypt 12- , bench "fourteen" $ nfIO $ botanBcrypt 14- , bench "sixteen" $ nfIO $ botanBcrypt 16- ]- ]- , bgroup "Hash"- [ bgroup "Crypton"- [ bench "password" $ nf cryptonHash password- , bench "plaintext" $ nf cryptonHash plaintext- , bench "longtext" $ nf cryptonHash longtext- ]- -- TODO: hashWithName is not exposed from Botan.Low.Hash. See issue #34.- {- , bgroup "Botan"- [ bench "password" $ nfIO $ botanHash password- , bench "plaintext" $ nfIO $ botanHash plaintext- , bench "longtext" $ nfIO $ botanHash longtext- ]- -}- ]- ]-
botan-low.cabal view
@@ -1,7 +1,7 @@-cabal-version: 3.0-name: botan-low-version: 0.0.2.0-synopsis: Low-level Botan bindings+cabal-version: 3.0+name: botan-low+version: 0.1.0.0+synopsis: Low-level Botan bindings description: Welcome to botan-low @@ -12,19 +12,18 @@ > X.509 certificates, modern AEAD ciphers, PKCS#11 and TPM hardware support, > password hashing, and post quantum crypto schemes. - For more information, see the [README on Github](https://github.com/haskellfoundation/botan)+ For more information, see the [README on Github](https://github.com/haskell-cryptography/botan) -author: Leo D.-maintainer: joris@well-typed.com, leo@apotheca.io-license: BSD-3-Clause-license-file: LICENSE+author: Leo D.+maintainer: joris@well-typed.com, leo@apotheca.io+license: BSD-3-Clause+license-file: LICENSE copyright: Copyright (c) 2023-2024, Apotheca Labs Copyright (c) 2024-2025, Haskell Foundation -build-type: Simple-category: Cryptography-+build-type: Simple+category: Cryptography -- NOTE: Use data-files instead, if this is inappropriate -- NOTE: This causes a soft error: -- Ignoring trailing fields after sections: "extra-source-files"@@ -40,53 +39,64 @@ README.md tested-with:- GHC ==9.2 || ==9.4 || ==9.6 || ==9.8 || ==9.10 || ==9.12+ ghc ==9.2 || ==9.4 || ==9.6 || ==9.8 || ==9.10 || ==9.12 source-repository head- type: git- location: https://github.com/haskellfoundation/botan- subdir: botan-low+ type: git+ location: https://github.com/haskell-cryptography/botan+ subdir: botan-low source-repository this- type: git- location: https://github.com/haskellfoundation/botan- subdir: botan-low- tag: botan-low-0.0.2.0+ type: git+ location: https://github.com/haskell-cryptography/botan+ subdir: botan-low+ tag: botan-low-0.1.0.0 -flag XFFI- description: Enable experimental / upstream ffi support- manual: True- default: False+common warnings+ ghc-options:+ -Wall+ -Wcompat+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wpartial-fields+ -Widentities+ -Wredundant-constraints+ -Wmissing-export-lists+ -Wno-unticked-promoted-constructors+ -Wunused-packages+ -Wmissing-deriving-strategies -library- hs-source-dirs: src- default-language: Haskell2010+common language+ default-language: GHC2021 default-extensions:- ExistentialQuantification- NoImplicitPrelude- OverloadedStrings+ DerivingStrategies+ DuplicateRecordFields+ LambdaCase+ NoFieldSelectors+ OverloadedRecordDot PatternSynonyms- RankNTypes- ScopedTypeVariables- TupleSections- TypeApplications+ RoleAnnotations+ ViewPatterns +library+ import: warnings, language+ hs-source-dirs: src exposed-modules: Botan.Low.Bcrypt Botan.Low.BlockCipher Botan.Low.Cipher Botan.Low.Error Botan.Low.FPE- Botan.Low.Hash Botan.Low.HOTP+ Botan.Low.Hash Botan.Low.KDF Botan.Low.KeyWrap Botan.Low.MAC Botan.Low.MPI Botan.Low.PubKey- Botan.Low.PubKey.Decrypt Botan.Low.PubKey.DH Botan.Low.PubKey.DSA+ Botan.Low.PubKey.Decrypt Botan.Low.PubKey.ECDH Botan.Low.PubKey.ECDSA Botan.Low.PubKey.Ed25519@@ -95,8 +105,8 @@ Botan.Low.PubKey.KeyAgreement Botan.Low.PubKey.KeyEncapsulation Botan.Low.PubKey.RSA- Botan.Low.PubKey.Sign Botan.Low.PubKey.SM2+ Botan.Low.PubKey.Sign Botan.Low.PubKey.Verify Botan.Low.PubKey.X25519 Botan.Low.PwdHash@@ -110,803 +120,77 @@ Botan.Low.ZFEC other-modules:+ Botan.Low.Error.Internal+ Botan.Low.Internal.ByteString+ Botan.Low.Internal.String Botan.Low.Make- Botan.Low.Prelude Botan.Low.Remake- Paths_botan_low - autogen-modules: Paths_botan_low build-depends:- , base >=4.16 && <4.22- , botan-bindings ^>=0.0 || ^>=0.1- , bytestring >=0.11 && <0.13- , deepseq >=1.1 && <2- , text >=1.2 && <1.3 || >=2.0 && <2.2-- if flag(xffi)- -- Botan.Low.X509.OCSP- exposed-modules:- Botan.Low.X509.CA- Botan.Low.X509.CRL- Botan.Low.X509.CSR- Botan.Low.X509.DN- Botan.Low.X509.Extensions- Botan.Low.X509.Options- Botan.Low.X509.Path- Botan.Low.X509.Store-- cpp-options: -DXFFI------- Unit tests-----test-suite botan-low-bcrypt-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/BcryptSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-block-cipher-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/BlockCipherSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-cipher-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/CipherSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-fpe-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/FPESpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-hash-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/HashSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-hotp-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/HOTPSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-kdf-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/KDFSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-keywrap-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/KeyWrapSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-mac-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/MACSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-mpi-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/MPISpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKeySpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-decrypt-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/DecryptSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-dh-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/DHSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-dsa-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/DSASpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-ecdh-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/ECDHSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-ecdsa-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/ECDSASpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-ed25519-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/Ed25519Spec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-elgamal-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/ElGamalSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-encrypt-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/EncryptSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-keyagreement-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/KeyAgreementSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-keyencapsulation-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/KeyEncapsulationSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-rsa-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/RSASpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-sign-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/SignSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-sm2-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/SM2Spec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-verify-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/VerifySpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-pubkey-x25519-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/PubKey/X25519Spec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings---- TODO: Pubkey folder tests--test-suite botan-low-pwdhash-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/PwdHashSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-rng-tests- if os(osx)- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False-- type: exitcode-stdio-1.0- main-is: Botan/Low/RNGSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-srp6-tests- -- TODO: temporarily disabled because the test suite fails. See issue #33.- buildable: False- type: exitcode-stdio-1.0- main-is: Botan/Low/SRP6Spec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-totp-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/TOTPSpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-utility-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/UtilitySpec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings--test-suite botan-low-x509-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/X509Spec.hs- hs-source-dirs: test/- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck-- other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings+ base >=4.16 && <4.22,+ botan-bindings ^>=0.2,+ bytestring >=0.11 && <0.13,+ deepseq >=1.1 && <2,+ text >=1.2 && <1.3 || >=2.0 && <2.2, -test-suite botan-low-zfec-tests- type: exitcode-stdio-1.0- main-is: Botan/Low/ZFECSpec.hs- hs-source-dirs: test/+test-suite test+ import: warnings, language+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test/ build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , hspec- , QuickCheck+ QuickCheck,+ async,+ base,+ botan-bindings,+ botan-low,+ bytestring,+ containers,+ hspec,+ tasty,+ tasty-hspec,+ tasty-hunit,+ tasty-quickcheck,+ text, other-modules:- Paths_botan_low- Test.Prelude-- autogen-modules: Paths_botan_low- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings------- Benchmarks-----benchmark botan-low-bench- hs-source-dirs: bench- main-is: Bcrypt.hs- type: exitcode-stdio-1.0- build-depends:- , base- , botan-bindings- , botan-low- , bytestring- , crypton- , tasty-bench-- default-language: Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings-- ghc-options: -with-rtsopts=-A32m+ Test.Botan.Low.Bcrypt+ Test.Botan.Low.BlockCipher+ Test.Botan.Low.Cipher+ Test.Botan.Low.FPE+ Test.Botan.Low.HOTP+ Test.Botan.Low.Hash+ Test.Botan.Low.KDF+ Test.Botan.Low.KeyWrap+ Test.Botan.Low.MAC+ Test.Botan.Low.MPI+ Test.Botan.Low.PubKey+ Test.Botan.Low.PubKey.DH+ Test.Botan.Low.PubKey.DSA+ Test.Botan.Low.PubKey.Decrypt+ Test.Botan.Low.PubKey.ECDH+ Test.Botan.Low.PubKey.ECDSA+ Test.Botan.Low.PubKey.Ed25519+ Test.Botan.Low.PubKey.ElGamal+ Test.Botan.Low.PubKey.Encrypt+ Test.Botan.Low.PubKey.KeyAgreement+ Test.Botan.Low.PubKey.KeyEncapsulation+ Test.Botan.Low.PubKey.RSA+ Test.Botan.Low.PubKey.SM2+ Test.Botan.Low.PubKey.Sign+ Test.Botan.Low.PubKey.Verify+ Test.Botan.Low.PubKey.X25519+ Test.Botan.Low.PwdHash+ Test.Botan.Low.RNG+ Test.Botan.Low.SRP6+ Test.Botan.Low.SRP6.Example+ Test.Botan.Low.TOTP+ Test.Botan.Low.Utility+ Test.Botan.Low.Version+ Test.Botan.Low.X509+ Test.Botan.Low.ZFEC+ Test.Util.ByteString+ Test.Util.Hspec - if impl(ghc >=8.6)- ghc-options: -fproc-alignment=64+ other-modules: Paths_botan_low+ autogen-modules: Paths_botan_low
src/Botan/Low/Bcrypt.hs view
@@ -11,41 +11,40 @@ Generate and validate Bcrypt password hashes -} -module Botan.Low.Bcrypt-(+module Botan.Low.Bcrypt ( --- * Bcrypt--- $introduction+ -- * Bcrypt+ -- $introduction --- * Usage--- $usage+ -- * Usage+ -- $usage --- * Generate a bcrypt digest- bcryptGenerate+ -- * Generate a bcrypt digest+ bcryptGenerate --- * Validate a bcrypt digest-, bcryptIsValid+ -- * Validate a bcrypt digest+ , bcryptIsValid --- * Work factor-, BcryptWorkFactor(..)-, pattern BcryptFast-, pattern BcryptGood-, pattern BcryptStrong-, BcryptPassword(..)-, BcryptDigest(..)+ -- * Work factor+ , BcryptWorkFactor+ , pattern BcryptFast+ , pattern BcryptGood+ , pattern BcryptStrong+ , BcryptPassword+ , BcryptDigest -) where+ ) where +import Botan.Bindings.Bcrypt+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.RNG+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Bindings.Bcrypt--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.RNG--import Data.ByteString.Internal as ByteString+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable {- $introduction @@ -151,10 +150,11 @@ False if the combination is not valid (but otherwise well formed), and otherwise throws an exception on error. -}-bcryptIsValid- :: BcryptPassword -- ^ __password__: The password to check against- -> BcryptDigest -- ^ __hash__: The stored hash to check against- -> IO Bool-bcryptIsValid password hash = asCString password $ \ passwordPtr -> do- asCString hash $ \ hashPtr -> do- throwBotanCatchingSuccess $ botan_bcrypt_is_valid (ConstPtr passwordPtr) (ConstPtr hashPtr)+bcryptIsValid ::+ BcryptPassword -- ^ __password__: The password to check against+ -> BcryptDigest -- ^ __hash__: The stored hash to check against+ -> IO Bool+bcryptIsValid password hash =+ asCString password $ \ passwordPtr ->+ asCString hash $ \ hashPtr ->+ throwBotanCatchingInvalidVerifier $ botan_bcrypt_is_valid (ConstPtr passwordPtr) (ConstPtr hashPtr)
src/Botan/Low/BlockCipher.hs view
@@ -8,179 +8,456 @@ Stability : experimental Portability : POSIX -This is a ‘raw’ interface to ECB mode block ciphers.-Most applications want the higher level cipher API which provides authenticated encryption.-This API exists as an escape hatch for applications which need to implement custom primitives using a PRP.+This module is based on the [Block+Ciphers](https://botan.randombit.net/handbook/api_ref/ffi.html#block-ciphers)+section of the C Botan FFI documentation, and on the [Block+Ciphers](https://botan.randombit.net/handbook/api_ref/block_cipher.html) section+of the C++ API reference. -} -module Botan.Low.BlockCipher-(---- * Block ciphers--- $introduction---- * Usage--- $usage-- BlockCipher(..)-, BlockCipherName(..)-, BlockCipher128Name(..)-, BlockCipherKey(..)-, BlockCipherCiphertext(..)-, withBlockCipher-, blockCipherInit-, blockCipherDestroy-, blockCipherName-, blockCipherBlockSize-, blockCipherGetKeyspec-, blockCipherSetKey-, blockCipherEncryptBlocks-, blockCipherDecryptBlocks-, blockCipherClear---- * 64-bit block ciphers--, pattern Blowfish-, pattern CAST128-, pattern DES-, pattern TripleDES-, pattern GOST_28147_89-, pattern IDEA---- * 128-bit block ciphers--, pattern AES128-, pattern AES192-, pattern AES256-, pattern ARIA128-, pattern ARIA192-, pattern ARIA256-, pattern Camellia128-, pattern Camellia192-, pattern Camellia256-, pattern Noekeon-, pattern SEED-, pattern SM4-, pattern Serpent-, pattern Twofish---- * 256-bit block ciphers--, pattern SHACAL2---- * 512-bit block ciphers--, pattern Threefish512---- * Convenience--, blockCiphers-, blockCipher128s-, allBlockCiphers+{-# LANGUAGE OverloadedStrings #-} -) where+module Botan.Low.BlockCipher (+ -- * Introduction+ -- $introduction+ -- * Example usage+ -- $usage+ -- * Block ciphers+ BlockCipher(..)+ , BlockCipherName+ , BlockCipherKey+ , BlockCipherCiphertext+ , withBlockCipher+ , blockCipherInit+ , blockCipherDestroy+ , blockCipherName+ , blockCipherBlockSize+ , blockCipherGetKeyspec+ , blockCipherSetKey+ , blockCipherEncryptBlocks+ , blockCipherDecryptBlocks+ , blockCipherClear+ -- * Available ciphers+ -- $available-ciphers+ , pattern AES128+ , pattern AES192+ , pattern AES256+ , pattern ARIA128+ , pattern ARIA192+ , pattern ARIA256+ , pattern Blowfish+ , pattern Camellia128+ , pattern Camellia192+ , pattern Camellia256+ , pattern Cascade+ , cascade+ , pattern CAST128+ , pattern DES+ , pattern TripleDES+ , pattern GOST_28147_89+ , pattern IDEA+ , pattern Lion+ , lion+ , pattern Noekeon+ , pattern SEED+ , pattern Serpent+ , pattern SHACAL2+ , pattern SM4+ , pattern Threefish512+ , pattern Twofish+ -- ** Convenience+ , blockCiphers+ , blockCipher128s+ , allBlockCiphers+ ) where -import qualified Data.ByteString as ByteString+import Botan.Bindings.BlockCipher+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.Make+import Botan.Low.Remake+import Control.Monad+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr -import Botan.Bindings.BlockCipher+{- $setup+>>> import Control.Monad+>>> import Botan.Low.Hash+-} -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+{-------------------------------------------------------------------------------+ Introduction+-------------------------------------------------------------------------------} {- $introduction -A `block cipher` is a deterministic, cryptographic primitive suitable for+A /block cipher/ is a deterministic, cryptographic primitive suitable for encrypting or decrypting a single, fixed-size block of data at a time. Block ciphers are used as building blocks for more complex cryptographic operations. If you are looking to encrypt user data, you are probably looking for-`Botan.Low.Cipher` instead.-+"Botan.Low.Cipher" instead. -} +{-------------------------------------------------------------------------------+ Example usage+-------------------------------------------------------------------------------}+ {- $usage -Unless you need a specific block cipher, it is strongly recommended that you use the `AES256` algorithm.+Unless you need a specific block cipher, it is strongly recommended that you use+the 'AES256' algorithm. -> import Botan.Low.BlockCipher-> blockCipher <- blockCipherInit AES256+>>> :{+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE OverloadedStrings #-}+import Botan.Low.BlockCipher+import Botan.Low.RNG+import Control.Exception+import Data.ByteString as BS+:} -To use a block cipher, we first need to generate (if we haven't already) a secret key.+For this example, we will use the 'AES256' algorithm. To use a block cipher, we+first need a secret key of the appropriate length. Such keys should be generated+securely, but to keep it simple in this example, we will just use a random+number+generator to generate a secret key. -> import Botan.Low.RNG-> rng <- rngInit "user"-> -- We will use the maximum key size; AES256 keys are always 16 bytes-> (_,keySize,_) <- blockCipherGetKeyspec blockCipher-> -- Block cipher keys are randomly generated-> key <- rngGet rng keySize+>>> :{+generateKey :: BlockCipher -> IO ByteString+generateKey blockCipher = do+ rng <- rngInit "user"+ (_,keySize,_) <- blockCipherGetKeyspec blockCipher+ key <- rngGet rng keySize+ pure key+:} -After the key is generated, we must set it as the block cipher key:+We can now initialise a block cipher object and set its secret key. -> blockCipherSetKey blockCipher key+>>> :{+initBlockCipher :: IO BlockCipher+initBlockCipher = do+ blockCipher <- blockCipherInit AES256+ key <- generateKey blockCipher+ blockCipherSetKey blockCipher key+ pure blockCipher+:} +Using the initialised block cipher, we can now encrypt a message. To encrypt a message, it must be a multiple of the block size.+To decrypt a message, simply reverse the process. As a sanity check,+we show that decryption after encryption yields the original message -> blockSize <- blockCipherBlockSize blockCipher-> -- AES256 block size is always 16 bytes-> message = "0000DEADBEEF0000" :: ByteString-> ciphertext <- blockCipherEncryptBlocks blockCipher message+>>> :{+roundtrip :: BlockCipher -> ByteString -> IO ()+roundtrip blockCipher message = do+ ciphertext <- blockCipherEncryptBlocks blockCipher message+ plaintext <- blockCipherDecryptBlocks blockCipher ciphertext+ print $ message == plaintext+:} -To decrypt a message, simply reverse the process:+Now we tie together the program. We initialise the block cipher+(and set its secret key). Then, we generate a message, and check that it+roundtrips through the encryption and decryption functions. -> plaintext <- blockCipherDecryptBlocks blockCipher ciphertext-> message == plaintext -- True+>>> :{+main :: IO ()+main = do+ blockCipher <- initBlockCipher+ blockSize <- blockCipherBlockSize blockCipher+ -- AES256 block size is always 16 bytes+ let message = "0000DEADBEEF0000"+ assert (BS.length message == blockSize) $ pure ()+ roundtrip blockCipher message+:} +All that is left to do is run the program.++>>> main+True+ -} +{-------------------------------------------------------------------------------+ Block ciphers+-------------------------------------------------------------------------------}+ -- | A mutable block cipher object newtype BlockCipher = MkBlockCipher { getBlockCipherForeignPtr :: ForeignPtr BotanBlockCipherStruct } -newBlockCipher :: BotanBlockCipher -> IO BlockCipher withBlockCipher :: BlockCipher -> (BotanBlockCipher -> IO a) -> IO a -- | Destroy a block cipher object immediately blockCipherDestroy :: BlockCipher -> IO () createBlockCipher :: (Ptr BotanBlockCipher -> IO CInt) -> IO BlockCipher-(newBlockCipher, withBlockCipher, blockCipherDestroy, createBlockCipher, _)+(withBlockCipher, blockCipherDestroy, createBlockCipher) = mkBindings- MkBotanBlockCipher runBotanBlockCipher- MkBlockCipher getBlockCipherForeignPtr+ MkBotanBlockCipher (.runBotanBlockCipher)+ MkBlockCipher (.getBlockCipherForeignPtr) botan_block_cipher_destroy --- | 128-bit block cipher name type-type BlockCipher128Name = BlockCipherName -pattern AES128- , AES192- , AES256- , ARIA128- , ARIA192- , ARIA256- , Camellia128- , Camellia192- , Camellia256- , Noekeon- , SEED- , SM4- , Serpent- , Twofish- :: BlockCipher128Name+-- | A block cipher secret key+type BlockCipherKey = ByteString -pattern AES128 = BOTAN_BLOCK_CIPHER_128_AES_128-pattern AES192 = BOTAN_BLOCK_CIPHER_128_AES_192-pattern AES256 = BOTAN_BLOCK_CIPHER_128_AES_256-pattern ARIA128 = BOTAN_BLOCK_CIPHER_128_ARIA_128-pattern ARIA192 = BOTAN_BLOCK_CIPHER_128_ARIA_192-pattern ARIA256 = BOTAN_BLOCK_CIPHER_128_ARIA_256-pattern Camellia128 = BOTAN_BLOCK_CIPHER_128_CAMELLIA_128-pattern Camellia192 = BOTAN_BLOCK_CIPHER_128_CAMELLIA_192-pattern Camellia256 = BOTAN_BLOCK_CIPHER_128_CAMELLIA_256-pattern Noekeon = BOTAN_BLOCK_CIPHER_128_NOEKEON-pattern SEED = BOTAN_BLOCK_CIPHER_128_SEED-pattern SM4 = BOTAN_BLOCK_CIPHER_128_SM4-pattern Serpent = BOTAN_BLOCK_CIPHER_128_SERPENT-pattern Twofish = BOTAN_BLOCK_CIPHER_128_TWOFISH+-- | A block cipher ciphertext+type BlockCipherCiphertext = ByteString +-- | Initialize a block cipher object+blockCipherInit ::+ BlockCipherName -- ^ __cipher_name__+ -> IO BlockCipher -- ^ __bc__+blockCipherInit = mkCreateObjectCString createBlockCipher botan_block_cipher_init++-- | Reinitializes a block cipher+--+-- You must call blockCipherSetKey in order to use the block cipher again.+blockCipherClear ::+ BlockCipher -- ^ __bc__: The cipher object+ -> IO ()+blockCipherClear = mkWithObjectAction withBlockCipher botan_block_cipher_clear++-- | Set the key for a block cipher+--+-- Throws an error if the key is not valid.+blockCipherSetKey ::+ BlockCipher -- ^ __bc__: The cipher object+ -> BlockCipherKey -- ^ __key[]__: A cipher key+ -> IO ()+blockCipherSetKey = mkSetBytesLen withBlockCipher $ \cipher key ->+ botan_block_cipher_set_key cipher (ConstPtr key)++-- | Return the block size of a block cipher.+blockCipherBlockSize ::+ BlockCipher -- ^ __bc__: The cipher object+ -> IO Int+blockCipherBlockSize = mkGetIntCode withBlockCipher botan_block_cipher_block_size++-- | Encrypt one or more blocks with a block cipher+--+-- Precondition: the length of the plaintext should be a multiple of the block+-- size, or this function throws an error.+blockCipherEncryptBlocks ::+ BlockCipher -- ^ __bc__: The cipher object+ -> ByteString -- ^ __in[]__: The plaintext+ -> IO BlockCipherCiphertext -- ^ __out[]__: The ciphertext+blockCipherEncryptBlocks blockCipher bytes = do+ blockSize <- blockCipherBlockSize blockCipher+ let (numBlocks,remBytes) = BS.length bytes `quotRem` blockSize+ when (remBytes /= 0) $+ error "blockCipherEncryptBlocks: length of the input plaintext is not a\+ \multiple of the block size"+ go (fromIntegral numBlocks)+ where+ go :: CSize -> IO BlockCipherCiphertext+ go numBlocks =+ withBlockCipher blockCipher $ \ blockCipherPtr ->+ asBytesLen bytes $ \ bytesPtr bytesLen ->+ allocBytes (fromIntegral bytesLen) $ \ destPtr ->+ throwBotanIfNegative_ $ botan_block_cipher_encrypt_blocks+ blockCipherPtr+ (ConstPtr bytesPtr)+ destPtr+ numBlocks++-- | Decrypt one or more blocks with a block cipher.+--+-- Precondition: the length of the ciphertext should be a multiple of the block size,+-- or this function throws an error.+--+-- If an incorrect key was set, the content of the decrypted plaintext is+-- unspecified.+blockCipherDecryptBlocks ::+ BlockCipher -- ^ __bc__: The cipher object+ -> BlockCipherCiphertext -- ^ __in[]__: The ciphertext+ -> IO ByteString -- ^ __out[]__: The plaintext+blockCipherDecryptBlocks blockCipher bytes = do+ blockSize <- blockCipherBlockSize blockCipher+ let (numBlocks,remBytes) = BS.length bytes `quotRem` blockSize+ when (remBytes /= 0) $+ error "blockCipherDecryptBlocks: length of the input plaintext is not a\+ \multiple of the block size"+ go (fromIntegral numBlocks)+ where+ go :: CSize -> IO ByteString+ go numBlocks =+ withBlockCipher blockCipher $ \ blockCipherPtr ->+ asBytesLen bytes $ \ bytesPtr bytesLen ->+ allocBytes (fromIntegral bytesLen) $ \ destPtr ->+ throwBotanIfNegative_ $ botan_block_cipher_decrypt_blocks+ blockCipherPtr+ (ConstPtr bytesPtr)+ destPtr+ numBlocks++-- | Get the name of a block cipher.+--+-- This function is not guaranteed to return the same exact value as used to initialize the context.+blockCipherName ::+ BlockCipher -- ^ __bc__+ -> IO BlockCipherName -- ^ __name__+blockCipherName = mkGetCString withBlockCipher botan_block_cipher_name++-- | Get the key specification of a block cipher+--+-- Returns the minimum, maximum, and modulo of valid keys.+blockCipherGetKeyspec ::+ BlockCipher -- ^ __bc__+ -> IO (Int,Int,Int) -- ^ __(min,max,mod)__+blockCipherGetKeyspec = mkGetSizes3 withBlockCipher botan_block_cipher_get_keyspec++{-------------------------------------------------------------------------------+ Available ciphers+-------------------------------------------------------------------------------}++{- $available-ciphers++A number of algorithms are available to be used as block ciphers. An algorithm+is selected by passing the /algorithm specification name/ of the algorithm as a+string to the block cipher functions. In some cases an algorithm specification+name is just the /algorithm name/, like @"AES-128"@. In other cases an algorithm+specification name is a combination of an algorithm name paired with some+parameters, such as @"Cascade(Serpent, AES-256)"@ where @"Cascade"@ is the+algorithm name followed by a 2-tuple of parameters. Pattern synonyms are+included for each available /algorithm name/. Where necessary, such as in the+@"Cascade"@ case, these algorithm names should be manually extended to full+algorithm specification names, or by using utility functions like 'cascade' that+this module also provides.++WARNING: 64-bit block ciphers should be avoided.++WARNING: the @Kuznyechik@ block ciphier is not included because its security is+disputed.++See the [Available+Ciphers](https://botan.randombit.net/handbook/api_ref/block_cipher.html#available-ciphers)+section of the C++ API reference for more information about available ciphers.++Each algorithm expects a certain size of block to encrypt or decrypt. For the+available algorithms, these block sizes in number of bits are listed below. For+algorithms that require parameters, the block size may vary depending on the+parameters. For the examples below, we pick some arbitrary parameters.++>>> :{+forM_ allBlockCiphers $ \name -> do+ let name' =+ if name == Cascade+ then cascade Serpent AES256+ else if name == Lion+ then lion SHA1 "RC4" (Just 64)+ else name+ blockCipher <- blockCipherInit name'+ sz <- blockCipherBlockSize blockCipher+ print (name, sz * 8)+:}+("AES-128",128)+("AES-192",128)+("AES-256",128)+("ARIA-128",128)+("ARIA-192",128)+("ARIA-256",128)+("Camellia-128",128)+("Camellia-192",128)+("Camellia-256",128)+("Noekeon",128)+("SEED",128)+("Serpent",128)+("SM4",128)+("Twofish",128)+("Blowfish",64)+("Cascade",128)+("CAST-128",64)+("DES",64)+("TripleDES",64)+("GOST-28147-89",64)+("IDEA",64)+("Lion",512)+("SHACAL2",256)+("Threefish-512",512)++-}++-- | Block cipher name type+type BlockCipherName = ByteString++pattern+ AES128+ , AES192+ , AES256+ , ARIA128+ , ARIA192+ , ARIA256+ , Blowfish+ , Camellia128+ , Camellia192+ , Camellia256+ , Cascade+ , CAST128+ , DES+ , TripleDES+ , GOST_28147_89+ , IDEA+ , Lion+ , Noekeon+ , SEED+ , Serpent+ , SHACAL2+ , SM4+ , Threefish512+ , Twofish+ :: BlockCipherName++pattern AES128 = BOTAN_BLOCK_CIPHER_AES_128+pattern AES192 = BOTAN_BLOCK_CIPHER_AES_192+pattern AES256 = BOTAN_BLOCK_CIPHER_AES_256+pattern ARIA128 = BOTAN_BLOCK_CIPHER_ARIA_128+pattern ARIA192 = BOTAN_BLOCK_CIPHER_ARIA_192+pattern ARIA256 = BOTAN_BLOCK_CIPHER_ARIA_256+pattern Blowfish = BOTAN_BLOCK_CIPHER_BLOWFISH+pattern Camellia128 = BOTAN_BLOCK_CIPHER_CAMELLIA_128+pattern Camellia192 = BOTAN_BLOCK_CIPHER_CAMELLIA_192+pattern Camellia256 = BOTAN_BLOCK_CIPHER_CAMELLIA_256+pattern Cascade = BOTAN_BLOCK_CIPHER_CASCADE++-- |+-- >>> cascade Serpent AES256+-- "Cascade(Serpent,AES-256)"+cascade :: BlockCipherName -> BlockCipherName -> BlockCipherName+cascade ciph1 ciph2 = Cascade /$ ciph1 <> "," <> ciph2++pattern CAST128 = BOTAN_BLOCK_CIPHER_CAST_128+pattern DES = BOTAN_BLOCK_CIPHER_DES+pattern TripleDES = BOTAN_BLOCK_CIPHER_TRIPLEDES+pattern GOST_28147_89 = BOTAN_BLOCK_CIPHER_GOST_28147_89+pattern IDEA = BOTAN_BLOCK_CIPHER_IDEA+pattern Lion = BOTAN_BLOCK_CIPHER_LION++-- |+-- >>> lion SHA1 "RC4" Nothing+-- "Lion(SHA-1,RC4)"+--+-- >>> lion SHA1 "RC4" (Just 64)+-- "Lion(SHA-1,RC4,64)"+lion :: HashName -> ByteString -> Maybe Word -> BlockCipherName+lion hashFun streamCiph optBlockSize =+ Lion /$ hashFun <> "," <> streamCiph <> case optBlockSize of+ Nothing -> ""+ Just blockSize -> "," <> BSC.pack (show blockSize)++pattern Noekeon = BOTAN_BLOCK_CIPHER_NOEKEON+pattern SEED = BOTAN_BLOCK_CIPHER_SEED+pattern Serpent = BOTAN_BLOCK_CIPHER_SERPENT+pattern SHACAL2 = BOTAN_BLOCK_CIPHER_SHACAL2+pattern SM4 = BOTAN_BLOCK_CIPHER_SM4+pattern Threefish512 = BOTAN_BLOCK_CIPHER_THREEFISH_512+pattern Twofish = BOTAN_BLOCK_CIPHER_TWOFISH++blockCipher128s :: [BlockCipherName] blockCipher128s = [ AES128 , AES192@@ -193,144 +470,24 @@ , Camellia256 , Noekeon , SEED- , SM4 , Serpent+ , SM4 , Twofish ] --- | Block cipher name type-type BlockCipherName = ByteString--pattern Blowfish- , CAST128- , DES- , TripleDES- , GOST_28147_89- , IDEA- , SHACAL2- , Threefish512- :: BlockCipherName--pattern Blowfish = BOTAN_BLOCK_CIPHER_BLOWFISH-pattern CAST128 = BOTAN_BLOCK_CIPHER_CAST_128-pattern DES = BOTAN_BLOCK_CIPHER_DES-pattern TripleDES = BOTAN_BLOCK_CIPHER_TRIPLEDES-pattern GOST_28147_89 = BOTAN_BLOCK_CIPHER_GOST_28147_89-pattern IDEA = BOTAN_BLOCK_CIPHER_IDEA-pattern SHACAL2 = BOTAN_BLOCK_CIPHER_SHACAL2-pattern Threefish512 = BOTAN_BLOCK_CIPHER_THREEFISH_512-+blockCiphers :: [BlockCipherName] blockCiphers = [ Blowfish+ , Cascade , CAST128 , DES , TripleDES , GOST_28147_89 , IDEA+ , Lion , SHACAL2 , Threefish512 ] allBlockCiphers :: [BlockCipherName] allBlockCiphers = blockCipher128s ++ blockCiphers---- | A block cipher secret key-type BlockCipherKey = ByteString---- | A block cipher ciphertext-type BlockCipherCiphertext = ByteString---- | Initialize a block cipher object-blockCipherInit- :: BlockCipherName -- ^ __cipher_name__- -> IO BlockCipher -- ^ __bc__-blockCipherInit = mkCreateObjectCString createBlockCipher botan_block_cipher_init---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withBlockCipherInit :: BlockCipherName -> (BlockCipher -> IO a) -> IO a-withBlockCipherInit = mkWithTemp1 blockCipherInit blockCipherDestroy--{- |-Reinitializes a block cipher--You must call blockCipherSetKey in order to use the block cipher again.--}-blockCipherClear- :: BlockCipher -- ^ __bc__: The cipher object- -> IO ()-blockCipherClear = mkWithObjectAction withBlockCipher botan_block_cipher_clear--{- |-Set the key for a block cipher--Throws an error if the key is not valid.--}-blockCipherSetKey- :: BlockCipher -- ^ __bc__: The cipher object- -> BlockCipherKey -- ^ __key[]__: A cipher key- -> IO ()-blockCipherSetKey = mkSetBytesLen withBlockCipher (\ cipher key -> botan_block_cipher_set_key cipher (ConstPtr key))---- | Return the block size of a block cipher.-blockCipherBlockSize- :: BlockCipher -- ^ __bc__: The cipher object- -> IO Int-blockCipherBlockSize = mkGetIntCode withBlockCipher botan_block_cipher_block_size--{- |-Encrypt one or more blocks with a block cipher--The plaintext length should be a multiple of the block size.--}-blockCipherEncryptBlocks- :: BlockCipher -- ^ __bc__: The cipher object- -> ByteString -- ^ __in[]__: The plaintext- -> IO BlockCipherCiphertext -- ^ __out[]__: The ciphertext-blockCipherEncryptBlocks blockCipher bytes = withBlockCipher blockCipher $ \ blockCipherPtr -> do- asBytesLen bytes $ \ bytesPtr bytesLen -> do- allocBytes (fromIntegral bytesLen) $ \ destPtr -> do- throwBotanIfNegative_ $ botan_block_cipher_encrypt_blocks- blockCipherPtr- (ConstPtr bytesPtr)- destPtr- bytesLen--{- |-Decrypt one or more blocks with a block cipher.--The ciphertext length should be a multiple of the block size.--If an incorrect key was set, the content of the decrypted plaintext is unspecified.--}-blockCipherDecryptBlocks- :: BlockCipher -- ^ __bc__: The cipher object- -> BlockCipherCiphertext -- ^ __in[]__: The ciphertext- -> IO ByteString -- ^ __out[]__: The plaintext-blockCipherDecryptBlocks blockCipher bytes = withBlockCipher blockCipher $ \ blockCipherPtr -> do- asBytesLen bytes $ \ bytesPtr bytesLen -> do- allocBytes (fromIntegral bytesLen) $ \ destPtr -> do- throwBotanIfNegative_ $ botan_block_cipher_decrypt_blocks- blockCipherPtr- (ConstPtr bytesPtr)- destPtr- bytesLen--{- |-Get the name of a block cipher.--This function is not guaranteed to return the same exact value as used to initialize the context.--}-blockCipherName- :: BlockCipher -- ^ __bc__- -> IO BlockCipherName -- ^ __name__-blockCipherName = mkGetCString withBlockCipher botan_block_cipher_name--{- |-Get the key specification of a block cipher--Returns the minimum, maximum, and modulo of valid keys.--}-blockCipherGetKeyspec- :: BlockCipher -- ^ __bc__- -> IO (Int,Int,Int) -- ^ __(min,max,mod)__-blockCipherGetKeyspec = mkGetSizes3 withBlockCipher botan_block_cipher_get_keyspec
src/Botan/Low/Cipher.hs view
@@ -14,101 +14,105 @@ repeatedly to encrypt an entire message. -} -module Botan.Low.Cipher-(---- * Cipher--- $introduction---- * Usage--- $usage+{-# LANGUAGE OverloadedStrings #-} - Cipher(..)-, CipherName(..)-, CipherKey(..)-, CipherNonce(..)-, CipherInitFlags(..)-, pattern MaskDirection-, pattern Encrypt-, pattern Decrypt-, CipherUpdateFlags(..)-, pattern CipherUpdate-, pattern CipherFinal-, withCipher-, cipherInit-, cipherDestroy-, cipherName-, cipherOutputLength-, cipherValidNonceLength-, cipherGetTagLength-, cipherGetDefaultNonceLength-, cipherGetUpdateGranularity-, cipherGetIdealUpdateGranularity-, cipherQueryKeylen-, cipherGetKeyspec-, cipherSetKey-, cipherReset-, cipherSetAssociatedData-, cipherStart-, cipherUpdate-, cipherEncrypt-, cipherDecrypt-, cipherClear+module Botan.Low.Cipher ( --- * Cipher modes-, CipherMode(..)-, cbcMode-, cfbMode-, cfbModeWith-, xtsMode+ -- * Cipher+ -- $introduction --- ** CBC padding-, CBCPaddingName(..)-, pattern PKCS7-, pattern OneAndZeros-, pattern X9_23-, pattern ESP-, pattern CTS-, pattern NoPadding+ -- * Usage+ -- $usage --- * AEAD-, AEADName(..)-, pattern ChaCha20Poly1305-, chaCha20Poly1305+ Cipher(..)+ , CipherName+ , CipherKey+ , CipherNonce+ , CipherInitFlags (..)+ , cipherInitFlags+ , CipherUpdateFlags (..)+ , cipherUpdateFlags+ , withCipher+ , cipherInit+ , cipherDestroy+ , cipherName+ , cipherOutputLength+ , cipherValidNonceLength+ , cipherGetTagLength+ , cipherGetDefaultNonceLength+ , cipherGetUpdateGranularity+ , cipherGetIdealUpdateGranularity+ , cipherQueryKeylen+ , cipherGetKeyspec+ , cipherSetKey+ , cipherReset+ , cipherSetAssociatedData+ , cipherStart+ , cipherUpdate+ , cipherEncrypt+ , cipherDecrypt+ , cipherClear --- * AEAD modes-, AEADMode(..)-, gcmMode-, gcmModeWith-, ocbMode-, ocbModeWith-, eaxMode-, eaxModeWith-, sivMode-, ccmMode-, ccmModeWith+ -- * Cipher modes+ , CipherMode+ , cbcMode+ , cfbMode+ , cfbModeWith+ , xtsMode --- * Convenience-, cipherEncryptOnline-, cipherDecryptOnline-, cipherModes-, cbcPaddings-, aeads-, allCiphers+ -- ** CBC padding+ , CBCPaddingName+ , pattern PKCS7+ , pattern OneAndZeros+ , pattern X9_23+ , pattern ESP+ , pattern CTS+ , pattern NoPadding -) where+ -- * AEAD+ , AEADName+ , pattern ChaCha20Poly1305+ , chaCha20Poly1305 -import qualified Data.ByteString as ByteString+ -- * AEAD modes+ , AEADMode+ , gcmMode+ , gcmModeWith+ , ocbMode+ , ocbModeWith+ , eaxMode+ , eaxModeWith+ , sivMode+ , ccmMode+ , ccmModeWith -import Botan.Bindings.Cipher+ -- * Convenience+ , cipherEncryptOnline+ , cipherDecryptOnline+ , cipherModes+ , cbcPaddings+ , aeads+ , allCiphers -import Botan.Low.BlockCipher-import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+ ) where -import Botan.Low.RNG+import Botan.Bindings.Cipher+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Low.BlockCipher+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.Make+import Botan.Low.Remake+import Control.Exception+import Data.ByteString (ByteString)+import qualified Data.ByteString as ByteString+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable {- $introduction @@ -193,19 +197,16 @@ newtype Cipher = MkCipher { getCipherForeignPtr :: ForeignPtr BotanCipherStruct } -newCipher :: BotanCipher -> IO Cipher withCipher :: Cipher -> (BotanCipher -> IO a) -> IO a -- | Destroy the cipher object immediately cipherDestroy :: Cipher -> IO () createCipher :: (Ptr BotanCipher -> IO CInt) -> IO Cipher-(newCipher, withCipher, cipherDestroy, createCipher, _)+(withCipher, cipherDestroy, createCipher) = mkBindings- MkBotanCipher runBotanCipher- MkCipher getCipherForeignPtr+ MkBotanCipher (.runBotanCipher)+ MkCipher (.getCipherForeignPtr) botan_cipher_destroy -type CipherInitFlags = Word32-type CipherUpdateFlags = Int type CipherNonce = ByteString type CipherKey = ByteString @@ -252,16 +253,16 @@ type AEADMode = ByteString -gcmMode :: BlockCipher128Name -> AEADName+gcmMode :: BlockCipherName -> AEADName gcmMode bc = bc // BOTAN_AEAD_MODE_GCM -gcmModeWith :: BlockCipher128Name -> Int -> AEADName+gcmModeWith :: BlockCipherName -> Int -> AEADName gcmModeWith bc tagSz = gcmMode bc /$ showBytes tagSz -ocbMode :: BlockCipher128Name -> AEADName+ocbMode :: BlockCipherName -> AEADName ocbMode bc = bc // BOTAN_AEAD_MODE_OCB -ocbModeWith :: BlockCipher128Name -> Int -> AEADName+ocbModeWith :: BlockCipherName -> Int -> AEADName ocbModeWith bc tagSz = ocbMode bc /$ showBytes tagSz eaxMode :: BlockCipherName -> AEADName@@ -270,15 +271,16 @@ eaxModeWith :: BlockCipherName -> Int -> AEADName eaxModeWith bc tagSz = eaxMode bc /$ showBytes tagSz -sivMode :: BlockCipher128Name -> AEADName+sivMode :: BlockCipherName -> AEADName sivMode bc = bc // BOTAN_AEAD_MODE_SIV -ccmMode :: BlockCipher128Name -> AEADName+ccmMode :: BlockCipherName -> AEADName ccmMode bc = bc // BOTAN_AEAD_MODE_CCM -ccmModeWith :: BlockCipher128Name -> Int -> Int -> AEADName+ccmModeWith :: BlockCipherName -> Int -> Int -> AEADName ccmModeWith bc tagSz l = ccmMode bc /$ showBytes tagSz <> "," <> showBytes l +cbcPaddings :: [CBCPaddingName] cbcPaddings = [ PKCS7 , OneAndZeros@@ -288,12 +290,14 @@ , NoPadding ] +cipherModes :: [CipherName] cipherModes = concat [ [ cbcMode bc pd | bc <- allBlockCiphers, pd <- cbcPaddings ] , [ cfbMode bc | bc <- allBlockCiphers ] , [ xtsMode bc | bc <- allBlockCiphers ] ] +aeads :: [AEADName] aeads = concat [ [ chaCha20Poly1305 ] , [ gcmMode bc | bc <- blockCipher128s ]@@ -303,36 +307,35 @@ , [ ccmMode bc | bc <- blockCipher128s ] ] +allCiphers :: [CipherName] allCiphers = cipherModes ++ aeads --- TODO: Rename CipherMaskDirection, CipherEncrypt, CipherDecrypt;--- Leave slim terminology for botan-pattern MaskDirection- , Encrypt -- ^ May be renamed Encipher to avoid confusion with PKEncrypt- , Decrypt -- ^ May be renamed Decipher to avoid confusion with PKDecrypt- :: CipherInitFlags+data CipherInitFlags =+ CipherMaskDirection+ | CipherEncrypt+ | CipherDecrypt+ deriving stock (Show, Eq) -pattern MaskDirection = BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION-pattern Encrypt = BOTAN_CIPHER_INIT_FLAG_ENCRYPT-pattern Decrypt = BOTAN_CIPHER_INIT_FLAG_DECRYPT+cipherInitFlags :: CipherInitFlags -> Word32+cipherInitFlags CipherMaskDirection = BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION+cipherInitFlags CipherEncrypt = BOTAN_CIPHER_INIT_FLAG_ENCRYPT+cipherInitFlags CipherDecrypt = BOTAN_CIPHER_INIT_FLAG_DECRYPT -pattern CipherUpdate- , CipherFinal- :: CipherUpdateFlags+data CipherUpdateFlags =+ CipherUpdate+ | CipherFinal+ deriving stock (Show, Eq) -pattern CipherUpdate = BOTAN_CIPHER_UPDATE_FLAG_NONE-pattern CipherFinal = BOTAN_CIPHER_UPDATE_FLAG_FINAL+cipherUpdateFlags :: CipherUpdateFlags -> Word32+cipherUpdateFlags CipherUpdate = BOTAN_CIPHER_UPDATE_FLAG_NONE+cipherUpdateFlags CipherFinal = BOTAN_CIPHER_UPDATE_FLAG_FINAL -- |Initialize a cipher object cipherInit :: CipherName -- ^ __name__ -> CipherInitFlags -- ^ __flags__ -> IO Cipher -- ^ __cipher__-cipherInit = mkCreateObjectCString1 createCipher botan_cipher_init---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withCipherInit :: CipherName -> CipherInitFlags -> (Cipher -> IO a) -> IO a-withCipherInit = mkWithTemp2 cipherInit cipherDestroy+cipherInit name flags = mkCreateObjectCString1 createCipher botan_cipher_init name (cipherInitFlags flags) -- |Return the name of the cipher object cipherName@@ -446,31 +449,50 @@ -- https://github.com/randombit/botan/blob/72dc18bbf598f2c3bef81a4fb2915e9c3c524ac4/src/lib/ffi/ffi_cipher.cpp#L133 -- -- Some ciphers (ChaChaPoly, EAX) may consume less input than the reported ideal granularity-cipherUpdate- :: Cipher -- ^ __cipher__- -> CipherUpdateFlags -- ^ __flags__- -> Int -- ^ __output_size__- -> ByteString -- ^ __input_bytes[]__- -> IO (Int,ByteString) -- ^ __(input_consumed,output[])__-cipherUpdate ctx flags outputSz input = withCipher ctx $ \ ctxPtr -> do- unsafeAsBytesLen input $ \ inputPtr inputSz -> do- alloca $ \ consumedPtr -> do- alloca $ \ writtenPtr -> do- output <- allocBytes outputSz $ \ outputPtr -> do- throwBotanIfNegative_ $ botan_cipher_update- ctxPtr- (fromIntegral flags)- outputPtr- (fromIntegral outputSz)- writtenPtr- (ConstPtr inputPtr)- inputSz- consumedPtr- consumed <- fromIntegral <$> peek consumedPtr- written <- fromIntegral <$> peek writtenPtr- -- NOTE: The safety of this function is suspect - may require deepseq- let processed = ByteString.take written output- in processed `seq` return (consumed,processed)+cipherUpdate ::+ Cipher -- ^ __cipher__+ -> CipherUpdateFlags -- ^ __flags__+ -> Int -- ^ __output_size__+ -> ByteString -- ^ __input_bytes[]__+ -> IO (Int,ByteString) -- ^ __(input_consumed,output[])__+cipherUpdate ctx flags outputSz input =+ withCipher ctx $ \ ctxPtr ->+ unsafeAsBytesLen input $ \ inputPtr inputSz ->+ alloca $ \ consumedPtr ->+ alloca $ \ writtenPtr -> do+ eithOutput <-+ try $ allocBytes outputSz $ \ outputPtr ->do+ throwBotanIfNegative_ $ botan_cipher_update+ ctxPtr+ (cipherUpdateFlags flags)+ outputPtr+ (fromIntegral outputSz)+ writtenPtr+ (ConstPtr inputPtr)+ inputSz+ consumedPtr+ -- If inssuficient buffer space, try again+ output <- case eithOutput of+ Left InsufficientBufferSpaceException{} -> do+ outputSz' <- peek writtenPtr+ allocBytes (fromIntegral outputSz') $ \ outputPtr ->do+ throwBotanIfNegative_ $ botan_cipher_update+ ctxPtr+ (cipherUpdateFlags flags)+ outputPtr+ outputSz'+ writtenPtr+ (ConstPtr inputPtr)+ -- No input should be provided on the second try if the first+ -- try had the FINAL flags set+ (if flags == CipherFinal then 0 else inputSz)+ consumedPtr+ Right bs -> pure bs+ consumed <- fromIntegral <$> peek consumedPtr+ written <- fromIntegral <$> peek writtenPtr+ -- NOTE: The safety of this function is suspect - may require deepseq+ let processed = ByteString.take written output+ in processed `seq` return (consumed,processed) {- | Encrypt and finalize a complete piece of data.@@ -504,7 +526,7 @@ o <- cipherOutputLength ctx input -- NOTE: Flawed but usable u <- cipherGetUpdateGranularity ctx -- TODO: When u == 1, it should be just input + t, right? t <- cipherGetTagLength ctx- if flags == BOTAN_CIPHER_INIT_FLAG_ENCRYPT+ if flags == CipherEncrypt then return (o + u + t) else return (o + u - t) -- TODO: Maybe just 'o'... @@ -539,15 +561,15 @@ cipherProcessOffline ctx flags msg = do o <- cipherEstimateOutputLength ctx flags (ByteString.length msg) -- snd <$> cipherUpdate ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL o msg- fst <$> cipherProcess ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL o msg+ fst <$> cipherProcess ctx CipherFinal o msg {-# WARNING cipherEncryptOffline "May be renamed to cipherEncrypt, may be moved to botan" #-} cipherEncryptOffline :: Cipher -> ByteString -> IO ByteString-cipherEncryptOffline ctx = cipherProcessOffline ctx BOTAN_CIPHER_INIT_FLAG_ENCRYPT+cipherEncryptOffline ctx = cipherProcessOffline ctx CipherEncrypt {-# WARNING cipherDecryptOffline "May be renamed to cipherDecrypt, may be moved to botan" #-} cipherDecryptOffline :: Cipher -> ByteString -> IO ByteString-cipherDecryptOffline ctx = cipherProcessOffline ctx BOTAN_CIPHER_INIT_FLAG_DECRYPT+cipherDecryptOffline ctx = cipherProcessOffline ctx CipherDecrypt {- Experiments with online processing@@ -570,14 +592,6 @@ -- encrest <- go (i + g) u t g rest -- return $! encblock : encrest --- NOTE: Some ciphers (SIV, CCM) are not online-capable algorithms, but Botan does not throw--- an error even though it should.-{-# DEPRECATED cipherProcessOnline "Moving from botan-low to botan" #-}-cipherProcessOnline :: Cipher -> CipherInitFlags -> ByteString -> IO ByteString-cipherProcessOnline ctx flags = if flags == BOTAN_CIPHER_INIT_FLAG_ENCRYPT- then cipherEncryptOnline ctx- else cipherDecryptOnline ctx- -- TODO: Consolidate online encipher / decipher -- TODO: Use Builder to do this -- https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Builder.html@@ -589,11 +603,11 @@ where go i g bs = case ByteString.splitAt g bs of (block,"") -> do- o <- cipherEstimateFinalOutputLength ctx BOTAN_CIPHER_INIT_FLAG_ENCRYPT i (ByteString.length block)- (processed,_) <- cipherProcess ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL o block+ o <- cipherEstimateFinalOutputLength ctx CipherEncrypt i (ByteString.length block)+ (processed,_) <- cipherProcess ctx CipherFinal o block return [processed] (block,rest) -> do- (processed,remaining) <- cipherProcess ctx BOTAN_CIPHER_UPDATE_FLAG_NONE g block+ (processed,remaining) <- cipherProcess ctx CipherUpdate g block (processed :) <$> go (i + g) g (remaining <> rest) -- Though this following version may be more efficient especially with lazy bytestrings -- or builder, though note *which* update function it uses - the original@@ -610,9 +624,9 @@ where go i g t bs = case ByteString.splitAt g bs of (block,"") -> do- o <- cipherEstimateFinalOutputLength ctx BOTAN_CIPHER_INIT_FLAG_DECRYPT i (ByteString.length block)- (processed,_) <- cipherProcess ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL o block+ o <- cipherEstimateFinalOutputLength ctx CipherDecrypt i (ByteString.length block)+ (processed,_) <- cipherProcess ctx CipherFinal o block return [processed] (block,rest) -> do- (processed,remaining) <- cipherProcess ctx BOTAN_CIPHER_UPDATE_FLAG_NONE g block+ (processed,remaining) <- cipherProcess ctx CipherUpdate g block (processed :) <$> go (i + g) g t (remaining <> rest)
src/Botan/Low/Error.hs view
@@ -7,401 +7,40 @@ Maintainer : joris@well-typed.com, leo@apotheca.io Stability : experimental Portability : POSIX--} -module Botan.Low.Error-( BotanErrorCode(..)-, pattern Success-, pattern InvalidIdentifier-, pattern InvalidInput-, pattern BadMAC-, pattern InsufficientBufferSpace-, pattern StringConversionError-, pattern ExceptionThrown-, pattern OutOfMemory-, pattern SystemError-, pattern InternalError-, pattern BadFlag-, pattern NullPointer-, pattern BadParameter-, pattern KeyNotSet-, pattern InvalidKeyLength-, pattern InvalidObjectState-, pattern NotImplemented-, pattern InvalidObject-, pattern TLSError-, pattern HttpError-, pattern RoughtimeError-, pattern UnknownError-, botanErrorDescription-, botanErrorLastExceptionMessage-, SomeBotanException(..)-, toBotanException-, fromBotanException-, InvalidInputException(..)-, BadMACException(..)-, InsufficientBufferSpaceException(..)-, StringConversionException(..)-, ExceptionThrownException(..)-, OutOfMemoryException(..)-, SystemErrorException(..)-, InternalErrorException(..)-, BadFlagException(..)-, NullPointerException(..)-, BadParameterException(..)-, KeyNotSetException(..)-, InvalidKeyLengthException(..)-, InvalidObjectStateException(..)-, NotImplementedException(..)-, InvalidObjectException(..)-, UnknownException(..)-, throwBotanError-, throwBotanIfNegative-, throwBotanIfNegative_-, throwBotanCatchingSuccess-, throwBotanCatchingBool-, throwBotanCatchingInt-, throwBotanErrorWithCallstack-, tryBotan-, catchBotan-, handleBotan-) where--import Data.Typeable--import Botan.Bindings.Error--import Botan.Low.Prelude--import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Char8 as Char8-import qualified Data.Text as Text---- | Botan error code data type-type BotanErrorCode = CInt--{--Botan error code patterns--}--pattern Success- , InvalidIdentifier- , InvalidInput- , BadMAC- , InsufficientBufferSpace- , StringConversionError- , ExceptionThrown- , OutOfMemory- , SystemError- , InternalError- , BadFlag- , NullPointer- , BadParameter- , KeyNotSet- , InvalidKeyLength- , InvalidObjectState- , NotImplemented- , InvalidObject- , TLSError- , HttpError- , RoughtimeError- , UnknownError- :: BotanErrorCode--pattern Success = BOTAN_FFI_SUCCESS-pattern InvalidIdentifier = BOTAN_FFI_INVALID_VERIFIER-pattern InvalidInput = BOTAN_FFI_ERROR_INVALID_INPUT-pattern BadMAC = BOTAN_FFI_ERROR_BAD_MAC-pattern InsufficientBufferSpace = BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE-pattern StringConversionError = BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR-pattern ExceptionThrown = BOTAN_FFI_ERROR_EXCEPTION_THROWN-pattern OutOfMemory = BOTAN_FFI_ERROR_OUT_OF_MEMORY-pattern SystemError = BOTAN_FFI_ERROR_SYSTEM_ERROR-pattern InternalError = BOTAN_FFI_ERROR_INTERNAL_ERROR-pattern BadFlag = BOTAN_FFI_ERROR_BAD_FLAG-pattern NullPointer = BOTAN_FFI_ERROR_NULL_POINTER-pattern BadParameter = BOTAN_FFI_ERROR_BAD_PARAMETER-pattern KeyNotSet = BOTAN_FFI_ERROR_KEY_NOT_SET-pattern InvalidKeyLength = BOTAN_FFI_ERROR_INVALID_KEY_LENGTH-pattern InvalidObjectState = BOTAN_FFI_ERROR_INVALID_OBJECT_STATE-pattern NotImplemented = BOTAN_FFI_ERROR_NOT_IMPLEMENTED-pattern InvalidObject = BOTAN_FFI_ERROR_INVALID_OBJECT-pattern TLSError = BOTAN_FFI_ERROR_TLS_ERROR-pattern HttpError = BOTAN_FFI_ERROR_HTTP_ERROR-pattern RoughtimeError = BOTAN_FFI_ERROR_ROUGHTIME_ERROR-pattern UnknownError = BOTAN_FFI_ERROR_UNKNOWN_ERROR--{--Botan error code functions--}---- | Convert an error code into a string. Returns "Unknown error" if the error code is not a known one.-botanErrorDescription :: BotanErrorCode -> IO ByteString-botanErrorDescription e = do- descPtr <- botan_error_description e- peekCString (unConstPtr descPtr)---- | Returns a static string stored in a thread local variable which contains the last exception message thrown.--- WARNING: This string buffer is overwritten on the next call to the FFI layer-botanErrorLastExceptionMessage :: IO ErrorMessage-botanErrorLastExceptionMessage = do- msgPtr <- botan_error_last_exception_message- peekCString (unConstPtr msgPtr)--type ErrorMessage = ByteString--{--Exceptions--}---- | The SomeBotanException type is the root of the botan exception type hierarchy.-data SomeBotanException = forall e . Exception e => SomeBotanException e--instance Show SomeBotanException where- show (SomeBotanException e) = show e--instance Exception SomeBotanException--toBotanException :: Exception e => e -> SomeException-toBotanException = toException . SomeBotanException--fromBotanException :: Exception e => SomeException -> Maybe e-fromBotanException x = do- SomeBotanException a <- fromException x- cast a--data InvalidVerifierException- = InvalidVerifierException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InvalidVerifierException where- toException = toBotanException- fromException = fromBotanException--data InvalidInputException- = InvalidInputException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InvalidInputException where- toException = toBotanException- fromException = fromBotanException--data BadMACException- = BadMACException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception BadMACException where- toException = toBotanException- fromException = fromBotanException--data InsufficientBufferSpaceException- = InsufficientBufferSpaceException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InsufficientBufferSpaceException where- toException = toBotanException- fromException = fromBotanException--data StringConversionException- = StringConversionException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception StringConversionException where- toException = toBotanException- fromException = fromBotanException--data ExceptionThrownException- = ExceptionThrownException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception ExceptionThrownException where- toException = toBotanException- fromException = fromBotanException--data OutOfMemoryException- = OutOfMemoryException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception OutOfMemoryException where- toException = toBotanException- fromException = fromBotanException--data SystemErrorException- = SystemErrorException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception SystemErrorException where- toException = toBotanException- fromException = fromBotanException--data InternalErrorException- = InternalErrorException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InternalErrorException where- toException = toBotanException- fromException = fromBotanException--data BadFlagException- = BadFlagException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception BadFlagException where- toException = toBotanException- fromException = fromBotanException--data NullPointerException- = NullPointerException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception NullPointerException where- toException = toBotanException- fromException = fromBotanException--data BadParameterException- = BadParameterException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception BadParameterException where- toException = toBotanException- fromException = fromBotanException--data KeyNotSetException- = KeyNotSetException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception KeyNotSetException where- toException = toBotanException- fromException = fromBotanException--data InvalidKeyLengthException- = InvalidKeyLengthException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InvalidKeyLengthException where- toException = toBotanException- fromException = fromBotanException--data InvalidObjectStateException- = InvalidObjectStateException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InvalidObjectStateException where- toException = toBotanException- fromException = fromBotanException--data NotImplementedException- = NotImplementedException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception NotImplementedException where- toException = toBotanException- fromException = fromBotanException--data InvalidObjectException- = InvalidObjectException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception InvalidObjectException where- toException = toBotanException- fromException = fromBotanException--data UnknownException- = UnknownException BotanErrorCode ErrorMessage CallStack- deriving (Show)--instance Exception UnknownException where- toException = toBotanException- fromException = fromBotanException--{--Throwing exceptions+This module is based on the [Return+codes](https://botan.randombit.net/handbook/api_ref/ffi.html#return-codes)+section of the C Botan FFI documentation. -} -throwBotanError :: HasCallStack => BotanErrorCode -> IO a-throwBotanError r = throwBotanErrorWithCallstack r callStack--throwBotanIfNegative :: HasCallStack => IO BotanErrorCode -> IO BotanErrorCode-throwBotanIfNegative act = do- e <- act- when (e < 0) $ throwBotanErrorWithCallstack (fromIntegral e) callStack- return e--throwBotanIfNegative_ :: HasCallStack => IO BotanErrorCode -> IO ()-throwBotanIfNegative_ act = do- e <- act- when (e < 0) $ throwBotanErrorWithCallstack (fromIntegral e) callStack---- TODO: Rename to throwBotanCatchingInvalidIdentifier, make:--- throwBotanCatchingSuccess :: HasCallStack => IO BotanErrorCode -> IO Bool--- throwBotanCatchingSuccess act = do--- result <- act--- case result of--- BOTAN_FFI_SUCCESS -> return True--- _ -> throwBotanErrorWithCallstack (fromIntegral result) callStack--- NOTE: Catches 0 / Success as True and 1 / InvalidIdentifier as False, throws all other values-throwBotanCatchingSuccess :: HasCallStack => IO BotanErrorCode -> IO Bool-throwBotanCatchingSuccess act = do- result <- act- case result of- BOTAN_FFI_SUCCESS -> return True- BOTAN_FFI_INVALID_VERIFIER -> return False- _ -> throwBotanErrorWithCallstack (fromIntegral result) callStack---- NOTE: Catches 1 as True and 0 as False, throws all other values-throwBotanCatchingBool :: HasCallStack => IO BotanErrorCode -> IO Bool-throwBotanCatchingBool act = do- result <- act- case result of- 0 -> return False- 1 -> return True- _ -> throwBotanErrorWithCallstack result callStack----- NOTE: Catches positive numbers including zero, throws all other values--- Equivalent to fromIntegral . throwBotanIfNegative-throwBotanCatchingInt :: HasCallStack => IO BotanErrorCode -> IO Int-throwBotanCatchingInt act = do- result <- act- when (result < 0) $ throwBotanErrorWithCallstack (fromIntegral result) callStack- return (fromIntegral result)--throwBotanErrorWithCallstack :: BotanErrorCode -> CallStack -> IO a-throwBotanErrorWithCallstack e cs = do- emsg <- botanErrorLastExceptionMessage- case e of- -- BOTAN_FFI_SUCCESS -> throwIO $ SUCCESS e cs- BOTAN_FFI_INVALID_VERIFIER -> throwIO $ InvalidVerifierException e emsg cs- BOTAN_FFI_ERROR_INVALID_INPUT -> throwIO $ InvalidInputException e emsg cs- BOTAN_FFI_ERROR_BAD_MAC -> throwIO $ BadMACException e emsg cs- BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE -> throwIO $ InsufficientBufferSpaceException e emsg cs- BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR -> throwIO $ StringConversionException e emsg cs- BOTAN_FFI_ERROR_EXCEPTION_THROWN -> throwIO $ ExceptionThrownException e emsg cs- BOTAN_FFI_ERROR_OUT_OF_MEMORY -> throwIO $ OutOfMemoryException e emsg cs- BOTAN_FFI_ERROR_SYSTEM_ERROR -> throwIO $ SystemErrorException e emsg cs- BOTAN_FFI_ERROR_INTERNAL_ERROR -> throwIO $ InternalErrorException e emsg cs- BOTAN_FFI_ERROR_BAD_FLAG -> throwIO $ BadFlagException e emsg cs- BOTAN_FFI_ERROR_NULL_POINTER -> throwIO $ NullPointerException e emsg cs- BOTAN_FFI_ERROR_BAD_PARAMETER -> throwIO $ BadParameterException e emsg cs- BOTAN_FFI_ERROR_KEY_NOT_SET -> throwIO $ KeyNotSetException e emsg cs- BOTAN_FFI_ERROR_INVALID_KEY_LENGTH -> throwIO $ InvalidKeyLengthException e emsg cs- BOTAN_FFI_ERROR_INVALID_OBJECT_STATE -> throwIO $ InvalidObjectStateException e emsg cs- BOTAN_FFI_ERROR_NOT_IMPLEMENTED -> throwIO $ NotImplementedException e emsg cs- BOTAN_FFI_ERROR_INVALID_OBJECT -> throwIO $ InvalidObjectException e emsg cs- _ -> throwIO $ UnknownException e emsg cs---- TODO: catchingBotan--- r0 <- botan_foo--- if r0 < 0--- then pure r0--- else do--- r1 <- botan_bar--tryBotan :: IO a -> IO (Either SomeBotanException a)-tryBotan = try--catchBotan :: IO a -> (SomeBotanException -> IO a) -> IO a-catchBotan = catch+module Botan.Low.Error (+ -- * Error codes+ Internal.BotanErrorCode+ , Internal.botanErrorDescription+ , Internal.BotanErrorMessage(..)+ , Internal.botanErrorLastExceptionMessage+ -- * Exception hierarchy+ , Internal.SomeBotanException(..)+ , Internal.toBotanException+ , Internal.fromBotanException+ -- * Individual exceptions+ , Internal.InvalidInputException(..)+ , Internal.BadMACException(..)+ , Internal.InsufficientBufferSpaceException(..)+ , Internal.StringConversionException(..)+ , Internal.ExceptionThrownException(..)+ , Internal.OutOfMemoryException(..)+ , Internal.SystemErrorException(..)+ , Internal.InternalErrorException(..)+ , Internal.BadFlagException(..)+ , Internal.NullPointerException(..)+ , Internal.BadParameterException(..)+ , Internal.KeyNotSetException(..)+ , Internal.InvalidKeyLengthException(..)+ , Internal.InvalidObjectStateException(..)+ , Internal.NotImplementedException(..)+ , Internal.InvalidObjectException(..)+ , Internal.UnknownException(..)+ ) where -handleBotan :: (SomeBotanException -> IO a) -> IO a -> IO a-handleBotan = flip catch+import Botan.Low.Error.Internal as Internal
+ src/Botan/Low/Error/Internal.hs view
@@ -0,0 +1,282 @@+{-|+Module : Botan.Low.Error+Description : Error codes and exception handling+Copyright : (c) 2023-2024, Apotheca Labs+ (c) 2024-2025, Haskell Foundation+License : BSD-3-Clause+Maintainer : joris@well-typed.com, leo@apotheca.io+Stability : experimental+Portability : POSIX+-}++{-# LANGUAGE DerivingVia #-}++module Botan.Low.Error.Internal (+ -- * Error codes+ BotanErrorCode+ , botanErrorDescription+ , BotanErrorMessage(..)+ , botanErrorLastExceptionMessage+ -- * Exception hierarchy+ , SomeBotanException(..)+ , toBotanException+ , fromBotanException+ -- ** Individual exceptions+ , InvalidInputException(..)+ , BadMACException(..)+ , InsufficientBufferSpaceException(..)+ , StringConversionException(..)+ , ExceptionThrownException(..)+ , OutOfMemoryException(..)+ , SystemErrorException(..)+ , InternalErrorException(..)+ , BadFlagException(..)+ , NullPointerException(..)+ , BadParameterException(..)+ , KeyNotSetException(..)+ , InvalidKeyLengthException(..)+ , InvalidObjectStateException(..)+ , NotImplementedException(..)+ , InvalidObjectException(..)+ , UnknownException(..)+ -- ** Throwing and catching+ , throwBotanError+ , throwBotanIfNegative+ , throwBotanIfNegative_+ , throwBotanCatchingInvalidVerifier+ , throwBotanCatchingInvalidInput+ , throwBotanCatchingBool+ , throwBotanCatching+ , throwBotanErrorWithCallstack+ ) where++import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.Error+import Botan.Low.Internal.ByteString+import Control.Exception+import Control.Monad+import Data.ByteString (ByteString)+import Data.Typeable+import Foreign.C.Types+import GHC.Stack+import Prelude++{-------------------------------------------------------------------------------+ Errors+-------------------------------------------------------------------------------}++newtype BotanErrorCode = BotanErrorCode CInt+ deriving stock (Show, Eq)++-- | Convert an error code into a string. Returns "Unknown error" if the error+-- code is not a known one.+botanErrorDescription :: BotanErrorCode -> IO ByteString+botanErrorDescription (BotanErrorCode e) = do+ descPtr <- botan_error_description e+ peekCString descPtr.unConstPtr++newtype BotanErrorMessage = BotanErrorMessage ByteString+ deriving newtype Show++-- | Returns a static string stored in a thread local variable which contains+-- the last exception message thrown. WARNING: This string buffer is+-- overwritten on the next call to the FFI layer+botanErrorLastExceptionMessage :: IO BotanErrorMessage+botanErrorLastExceptionMessage = do+ msgPtr <- botan_error_last_exception_message+ BotanErrorMessage <$> peekCString msgPtr.unConstPtr++{-------------------------------------------------------------------------------+ Exception hierarchy+-------------------------------------------------------------------------------}++-- | The SomeBotanException type is the root of the botan exception type+-- hierarchy.+data SomeBotanException = forall e . Exception e => SomeBotanException e++instance Show SomeBotanException where+ show (SomeBotanException e) = show e++instance Exception SomeBotanException++toBotanException :: Exception e => e -> SomeException+toBotanException = toException . SomeBotanException++fromBotanException :: Exception e => SomeException -> Maybe e+fromBotanException x = do+ SomeBotanException a <- fromException x+ cast a++newtype ViaSomeBotanException e = ViaSomeBotanException e+ deriving stock Show++instance (Show e, Typeable e) => Exception (ViaSomeBotanException e) where+ toException = toBotanException+ fromException = fromBotanException++{-------------------------------------------------------------------------------+ Individual exceptions+-------------------------------------------------------------------------------}++data InvalidVerifierException+ = InvalidVerifierException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InvalidVerifierException++data InvalidInputException+ = InvalidInputException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InvalidInputException++data BadMACException+ = BadMACException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException BadMACException++data InsufficientBufferSpaceException+ = InsufficientBufferSpaceException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InsufficientBufferSpaceException++data StringConversionException+ = StringConversionException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException StringConversionException++data ExceptionThrownException+ = ExceptionThrownException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException ExceptionThrownException++data OutOfMemoryException+ = OutOfMemoryException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException OutOfMemoryException++data SystemErrorException+ = SystemErrorException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException SystemErrorException++data InternalErrorException+ = InternalErrorException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InternalErrorException++data BadFlagException+ = BadFlagException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException BadFlagException++data NullPointerException+ = NullPointerException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException NullPointerException++data BadParameterException+ = BadParameterException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException BadParameterException++data KeyNotSetException+ = KeyNotSetException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException KeyNotSetException++data InvalidKeyLengthException+ = InvalidKeyLengthException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InvalidKeyLengthException++data InvalidObjectStateException+ = InvalidObjectStateException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InvalidObjectStateException++data NotImplementedException+ = NotImplementedException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException NotImplementedException++data InvalidObjectException+ = InvalidObjectException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException InvalidObjectException++data UnknownException+ = UnknownException BotanErrorCode BotanErrorMessage CallStack+ deriving stock (Show)+ deriving Exception via ViaSomeBotanException UnknownException++{-------------------------------------------------------------------------------+ Exceptions: throwing and catching+-------------------------------------------------------------------------------}++throwBotanError :: HasCallStack => CInt -> IO a+throwBotanError r = throwBotanErrorWithCallstack r callStack++throwBotanIfNegative :: HasCallStack => IO CInt -> IO CInt+throwBotanIfNegative act = do+ e <- act+ when (e < 0) $ throwBotanErrorWithCallstack e callStack+ return e++throwBotanIfNegative_ :: HasCallStack => IO CInt -> IO ()+throwBotanIfNegative_ act = do+ e <- act+ when (e < 0) $ throwBotanErrorWithCallstack e callStack++-- | 'throwBotanCatching' with @retCode = 'BOTAN_FFI_INVALID_VERIFIER'@.+throwBotanCatchingInvalidVerifier :: HasCallStack => IO CInt -> IO Bool+throwBotanCatchingInvalidVerifier = throwBotanCatching BOTAN_FFI_INVALID_VERIFIER++-- | 'throwBotanCatching' with @retCode = 'BOTAN_FFI_ERROR_INVALID_INPUT'@.+throwBotanCatchingInvalidInput :: HasCallStack => IO CInt -> IO Bool+throwBotanCatchingInvalidInput = throwBotanCatching BOTAN_FFI_ERROR_INVALID_INPUT++-- | @'throwBotanCatching' retCode action@ runs the @action@ and catches return+-- code 0 (Success) as True and @retCode@ as False. All other return codes are+-- thrown as exceptions.+throwBotanCatching :: HasCallStack => CInt -> IO CInt -> IO Bool+throwBotanCatching x act = do+ result <- act+ case result of+ BOTAN_FFI_SUCCESS -> return True+ _ | x == result -> return False+ | otherwise -> throwBotanErrorWithCallstack result callStack++-- NOTE: Catches 1 as True and 0 as False, throws all other values+throwBotanCatchingBool :: HasCallStack => IO CInt -> IO Bool+throwBotanCatchingBool act = do+ result <- act+ case result of+ 0 -> return False+ 1 -> return True+ _ -> throwBotanErrorWithCallstack result callStack++throwBotanErrorWithCallstack :: CInt -> CallStack -> IO a+throwBotanErrorWithCallstack e cs = do+ emsg <- botanErrorLastExceptionMessage+ case e of+ -- Note: we do not include a case for @BOTAN_FFI_SUCCESS@ since it+ -- signals that the function call was successful.+ BOTAN_FFI_INVALID_VERIFIER -> throwIO $ InvalidVerifierException bec emsg cs+ BOTAN_FFI_ERROR_INVALID_INPUT -> throwIO $ InvalidInputException bec emsg cs+ BOTAN_FFI_ERROR_BAD_MAC -> throwIO $ BadMACException bec emsg cs+ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE -> throwIO $ InsufficientBufferSpaceException bec emsg cs+ BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR -> throwIO $ StringConversionException bec emsg cs+ BOTAN_FFI_ERROR_EXCEPTION_THROWN -> throwIO $ ExceptionThrownException bec emsg cs+ BOTAN_FFI_ERROR_OUT_OF_MEMORY -> throwIO $ OutOfMemoryException bec emsg cs+ BOTAN_FFI_ERROR_SYSTEM_ERROR -> throwIO $ SystemErrorException bec emsg cs+ BOTAN_FFI_ERROR_INTERNAL_ERROR -> throwIO $ InternalErrorException bec emsg cs+ BOTAN_FFI_ERROR_BAD_FLAG -> throwIO $ BadFlagException bec emsg cs+ BOTAN_FFI_ERROR_NULL_POINTER -> throwIO $ NullPointerException bec emsg cs+ BOTAN_FFI_ERROR_BAD_PARAMETER -> throwIO $ BadParameterException bec emsg cs+ BOTAN_FFI_ERROR_KEY_NOT_SET -> throwIO $ KeyNotSetException bec emsg cs+ BOTAN_FFI_ERROR_INVALID_KEY_LENGTH -> throwIO $ InvalidKeyLengthException bec emsg cs+ BOTAN_FFI_ERROR_INVALID_OBJECT_STATE -> throwIO $ InvalidObjectStateException bec emsg cs+ BOTAN_FFI_ERROR_NOT_IMPLEMENTED -> throwIO $ NotImplementedException bec emsg cs+ BOTAN_FFI_ERROR_INVALID_OBJECT -> throwIO $ InvalidObjectException bec emsg cs+ _ -> throwIO $ UnknownException bec emsg cs+ where+ bec = BotanErrorCode e
src/Botan/Low/FPE.hs view
@@ -33,30 +33,30 @@ the number of words in the dictionary. -} -module Botan.Low.FPE-(-- FPE(..)-, FPEFlags(..)-, pattern FPENone-, pattern FPEFE1CompatMode-, withFPE-, fpeInitFE1-, fpeDestroy-, fpeEncrypt-, fpeDecrypt--) where+module Botan.Low.FPE ( -import qualified Data.ByteString as ByteString+ FPE(..)+ , FPEFlags(..)+ , fPEFlags+ , withFPE+ , fpeInitFE1+ , fpeDestroy+ , fpeEncrypt+ , fpeDecrypt -import Botan.Bindings.FPE+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.FPE+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.MPI+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr -- NOTE: This module lacks documentation, and is not mentioned in the FFI bindings. -- It is mentioned in the C++ docs, but the construction significantly differs.@@ -75,23 +75,22 @@ newtype FPE = MkFPE { getFPEForeignPtr :: ForeignPtr BotanFPEStruct } -newFPE :: BotanFPE -> IO FPE withFPE :: FPE -> (BotanFPE -> IO a) -> IO a fpeDestroy :: FPE -> IO () createFPE :: (Ptr BotanFPE -> IO CInt) -> IO FPE-(newFPE, withFPE, fpeDestroy, createFPE, _)+(withFPE, fpeDestroy, createFPE) = mkBindings- MkBotanFPE runBotanFPE- MkFPE getFPEForeignPtr+ MkBotanFPE (.runBotanFPE)+ MkFPE (.getFPEForeignPtr) botan_fpe_destroy -type FPEFlags = Word32+data FPEFlags =+ FPENone+ | FPEFE1CompatMode -pattern FPENone- , FPEFE1CompatMode- :: FPEFlags-pattern FPENone = BOTAN_FPE_FLAG_NONE-pattern FPEFE1CompatMode = BOTAN_FPE_FLAG_FE1_COMPAT_MODE+fPEFlags :: FPEFlags -> Word32+fPEFlags FPENone = BOTAN_FPE_FLAG_NONE+fPEFlags FPEFE1CompatMode = BOTAN_FPE_FLAG_FE1_COMPAT_MODE -- | Initialize a FE1 FPE context fpeInitFE1@@ -108,11 +107,7 @@ (ConstPtr keyPtr) keyLen (fromIntegral rounds)- flags---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withFPEInitFE1 :: MP -> ByteString -> Int -> FPEFlags -> (FPE -> IO a) -> IO a-withFPEInitFE1 = mkWithTemp4 fpeInitFE1 fpeDestroy+ (fPEFlags flags) -- -- NOTE: Referentially transparent, move to botan -- fpeEncrypt :: FPE -> MP -> ByteString -> IO MP@@ -155,7 +150,3 @@ withMP mp $ \ mpPtr -> do asBytesLen tweak $ \ tweakPtr tweakLen -> do throwBotanIfNegative_ $ botan_fpe_decrypt fpePtr mpPtr (ConstPtr tweakPtr) tweakLen--data FE1InitFlags- = FE1None -- BOTAN_FPE_FLAG_NONE- | FE1CompatMode -- BOTAN_FPE_FLAG_FE1_COMPAT_MODE
src/Botan/Low/HOTP.hs view
@@ -17,47 +17,51 @@ as a mobile phone. -} -module Botan.Low.HOTP-(---- * Hash-based One Time Password--- $introduction--- * Usage--- $usage---- * HOTP+module Botan.Low.HOTP ( - HOTP(..)-, HOTPHashName(..)-, HOTPCounter(..)-, HOTPCode(..)-, withHOTP-, hotpInit-, hotpDestroy-, hotpGenerate-, hotpCheck+ -- * Hash-based One Time Password+ -- $introduction+ -- * Usage+ -- $usage --- * HOTP Hashes+ -- * HOTP -, pattern HOTP_SHA1-, pattern HOTP_SHA256-, pattern HOTP_SHA512+ HOTP(..)+ , HOTPHashName+ , HOTPCounter+ , HOTPCode+ , withHOTP+ , hotpInit+ , hotpDestroy+ , hotpGenerate+ , hotpCheck --- * Convenience+ -- * HOTP Hashes -, hotpHashes+ , pattern HOTP_SHA1+ , pattern HOTP_SHA256+ , pattern HOTP_SHA512 -) where+ -- * Convenience -import qualified Data.ByteString as ByteString+ , hotpHashes -import Botan.Bindings.HOTP-import Botan.Low.Hash+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.HOTP+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Remake+import Control.Monad+import Data.ByteString (ByteString)+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- NOTE: RFC 4226 -- NOTE: I think this *only* takes SHA-2, specificaly "SHA-256" and "SHA-512",@@ -164,14 +168,13 @@ newtype HOTP = MkHOTP { getHOTPForeignPtr :: ForeignPtr BotanHOTPStruct } -newHOTP :: BotanHOTP -> IO HOTP withHOTP :: HOTP -> (BotanHOTP -> IO a) -> IO a hotpDestroy :: HOTP -> IO () createHOTP :: (Ptr BotanHOTP -> IO CInt) -> IO HOTP-(newHOTP, withHOTP, hotpDestroy, createHOTP, _)+(withHOTP, hotpDestroy, createHOTP) = mkBindings- MkBotanHOTP runBotanHOTP- MkHOTP getHOTPForeignPtr+ MkBotanHOTP (.runBotanHOTP)+ MkHOTP (.getHOTPForeignPtr) botan_hotp_destroy type HOTPHashName = HashName@@ -186,6 +189,7 @@ pattern HOTP_SHA512 = SHA512 -- TODO: Do any other hashes work?+hotpHashes :: [HOTPHashName] hotpHashes = [ HOTP_SHA1 , HOTP_SHA256@@ -210,10 +214,6 @@ (ConstPtr algoPtr) (fromIntegral digits) --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withHOTPInit :: ByteString -> ByteString -> Int -> (HOTP -> IO a) -> IO a-withHOTPInit = mkWithTemp3 hotpInit hotpDestroy- -- NOTE: User is responsible for incrementing counter at this level hotpGenerate :: HOTP -- ^ __hotp__@@ -221,7 +221,7 @@ -> IO HOTPCode -- ^ __hotp_code__ hotpGenerate hotp counter = withHOTP hotp $ \ hotpPtr -> do alloca $ \ outPtr -> do- throwBotanIfNegative $ botan_hotp_generate hotpPtr outPtr counter+ void $ throwBotanIfNegative $ botan_hotp_generate hotpPtr outPtr counter peek outPtr -- NOTE:@@ -229,14 +229,15 @@ -- invalid then always returns (false,starting_counter), since the -- last successful authentication counter has not changed. "" -- NOTE: "Depending on the environment a resync_range of 3 to 10 might be appropriate."-hotpCheck- :: HOTP -- ^ __hotp__- -> HOTPCode -- ^ __hotp_code__- -> HOTPCounter -- ^ __hotp_counter__- -> Int -- ^ __resync_range__- -> IO (Bool, HOTPCounter) -- ^ __(valid,next_counter)__-hotpCheck hotp code counter resync = withHOTP hotp $ \ hotpPtr -> do+hotpCheck ::+ HOTP -- ^ __hotp__+ -> HOTPCode -- ^ __hotp_code__+ -> HOTPCounter -- ^ __hotp_counter__+ -> Int -- ^ __resync_range__+ -> IO (Bool, HOTPCounter) -- ^ __(valid,next_counter)__+hotpCheck hotp code counter resync =+ withHOTP hotp $ \ hotpPtr -> alloca $ \ outPtr -> do- valid <- throwBotanCatchingSuccess $ botan_hotp_check hotpPtr outPtr code counter (fromIntegral resync)- nextCounter <- peek outPtr- return (valid, nextCounter)+ valid <- throwBotanCatchingInvalidVerifier $ botan_hotp_check hotpPtr outPtr code counter (fromIntegral resync)+ nextCounter <- peek outPtr+ return (valid, nextCounter)
src/Botan/Low/Hash.hs view
@@ -26,83 +26,84 @@ hashFinal), the internal state is reset to begin hashing a new message. -} -module Botan.Low.Hash-(---- * Hashing--- $introduction---- * Usage--- $usage+{-# LANGUAGE OverloadedStrings #-} - Hash(..)-, HashName(..)-, HashDigest(..)-, withHash-, hashInit-, hashDestroy-, hashName-, hashBlockSize-, hashOutputLength-, hashCopyState-, hashUpdate-, hashFinal-, hashUpdateFinalize-, hashUpdateFinalizeClear-, hashClear+module Botan.Low.Hash ( --- * Hash algorithms+ -- * Hashing+ -- $introduction -, pattern BLAKE2b-, blake2b-, pattern Keccak1600-, keccak1600-, pattern GOST_34_11-, pattern MD4-, pattern MD5-, pattern RIPEMD160-, pattern SHA1-, pattern SHA224-, pattern SHA256-, pattern SHA384-, pattern SHA512-, pattern SHA512_256-, pattern SHA3-, sha3-, pattern SHAKE128-, shake128-, pattern SHAKE256-, shake256-, pattern SM3-, pattern Skein512-, skein512-, pattern Streebog256-, pattern Streebog512-, pattern Whirlpool-, pattern Parallel-, pattern Comb4P-, pattern Adler32-, pattern CRC24-, pattern CRC32+ -- * Usage+ -- $usage --- * Convenience+ Hash(..)+ , HashName+ , HashDigest+ , withHash+ , hashInit+ , hashDestroy+ , hashName+ , hashBlockSize+ , hashOutputLength+ , hashCopyState+ , hashUpdate+ , hashFinal+ , hashUpdateFinalize+ , hashUpdateFinalizeClear+ , hashClear -, cryptohashes-, checksums-, allHashes+ -- * Hash algorithms -) where+ , pattern BLAKE2b+ , blake2b+ , pattern Keccak1600+ , keccak1600+ , pattern GOST_34_11+ , pattern MD4+ , pattern MD5+ , pattern RIPEMD160+ , pattern SHA1+ , pattern SHA224+ , pattern SHA256+ , pattern SHA384+ , pattern SHA512+ , pattern SHA512_256+ , pattern SHA3+ , sha3+ , pattern SHAKE128+ , shake128+ , pattern SHAKE256+ , shake256+ , pattern SM3+ , pattern Skein512+ , skein512+ , pattern Streebog256+ , pattern Streebog512+ , pattern Whirlpool+ , pattern Parallel+ , pattern Comb4P+ , pattern Adler32+ , pattern CRC24+ , pattern CRC32 -import qualified Data.ByteString as ByteString+ -- * Convenience -import System.IO.Unsafe+ , cryptohashes+ , checksums+ , allHashes -import Botan.Bindings.Hash+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+import Botan.Bindings.Hash+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.Make+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr {- $introduction @@ -140,14 +141,13 @@ newtype Hash = MkHash { getHashForeignPtr :: ForeignPtr BotanHashStruct } -newHash :: BotanHash -> IO Hash withHash :: Hash -> (BotanHash -> IO a) -> IO a hashDestroy :: Hash -> IO () createHash :: (Ptr BotanHash -> IO CInt) -> IO Hash-(newHash, withHash, hashDestroy, createHash, _)+(withHash, hashDestroy, createHash) = mkBindings- MkBotanHash runBotanHash- MkHash getHashForeignPtr+ MkBotanHash (.runBotanHash)+ MkHash (.getHashForeignPtr) botan_hash_destroy type HashName = ByteString@@ -182,10 +182,12 @@ pattern BLAKE2b = BOTAN_HASH_BLAKE2B -- TODO: function -- sz is digest size in bits, must be 1-64 bytes, eg: 8-512 in multiples of 8+blake2b :: (Ord a, Num a, Show a) => a -> HashName blake2b sz | sz <= 512 = BLAKE2b /$ showBytes sz blake2b _ = error "Invalid BLAKE2b variant" pattern Keccak1600 = BOTAN_HASH_KECCAK_1600 -- TODO: function or pattern+keccak1600 :: (Eq a, Num a, Show a) => a -> HashName keccak1600 n | n `elem` [224, 256, 384, 512] = Keccak1600 /$ showBytes n keccak1600 _ = error "Invalid Keccak-1600 variant" -- pattern Keccak1600_224 = "Keccak-1600(224)"@@ -204,6 +206,7 @@ pattern SHA512_256 = BOTAN_HASH_SHA_512_256 pattern SHA3 = BOTAN_HASH_SHA_3 -- TODO: function or pattern+sha3 :: (Eq a, Num a, Show a) => a -> HashName sha3 n | n `elem` [224, 256, 384, 512] = SHA3 /$ showBytes n sha3 _ = error "Invalid SHA-3 variant" -- pattern SHA3_224 = "SHA-3(224)"@@ -212,13 +215,16 @@ -- pattern SHA3_512 = "SHA-3(512)" pattern SHAKE128 = BOTAN_HASH_SHAKE_128 -- TODO: function+shake128 :: Show a => a -> HashName shake128 sz = SHAKE128 /$ showBytes sz pattern SHAKE256 = BOTAN_HASH_SHAKE_256 -- TODO: function+shake256 :: Show a => a -> HashName shake256 sz = SHAKE256 /$ showBytes sz pattern SM3 = BOTAN_HASH_SM3 pattern Skein512 = BOTAN_HASH_SKEIN_512 -- TODO: function+skein512 :: Show a => a -> HashName -> HashName skein512 sz salt = Skein512 /$ showBytes sz <> "," <> salt pattern Streebog256 = BOTAN_HASH_STREEBOG_256 pattern Streebog512 = BOTAN_HASH_STREEBOG_512@@ -260,12 +266,12 @@ -- , "SHA-3(384)" -- , "SHA-3(512)" -- NOTE: SHAKE-128 has no default value, a parameter *MUST* be supplied- , shake128 128+ , shake128 @Int 128 -- , "SHAKE-128(128)" -- , "SHAKE-128(256)" -- , "SHAKE-128(512)" -- NOTE: SHAKE-256 has no default value, a parameter *MUST* be supplied- , shake256 128+ , shake256 @Int 128 -- , "SHAKE-256(128)" -- , "SHAKE-256(256)" -- , "SHAKE-256(512)"@@ -283,12 +289,14 @@ -- hashStrategies :: [HashName] -- hashStrategies = undefined +checksums :: [HashName] checksums = [ Adler32 , CRC24 , CRC32 ] +allHashes :: [HashName] allHashes = cryptohashes ++ checksums type HashDigest = ByteString@@ -298,10 +306,6 @@ -> IO Hash -- ^ __hash__: hash object hashInit = mkCreateObjectCString createHash (\ out name -> botan_hash_init out name 0) --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withHashInit :: HashName -> (Hash -> IO a) -> IO a-withHashInit = mkWithTemp1 hashInit hashDestroy- hashName :: Hash -- ^ __hash__: the object to read -> IO HashDigest -- ^ __name__: output buffer@@ -366,11 +370,3 @@ hashClear ctx return dg -- Or: hashUpdateFinalize ctx bytes <* hashClear ctx--hashWithHash :: Hash -> ByteString -> IO HashDigest-hashWithHash = hashUpdateFinalizeClear--hashWithName :: HashName -> ByteString -> IO HashDigest-hashWithName name bytes = do- ctx <- hashInit name- hashWithHash ctx bytes
+ src/Botan/Low/Internal/ByteString.hs view
@@ -0,0 +1,107 @@+module Botan.Low.Internal.ByteString (+ peekCString+ , withCString+ , withCBytes+ , withCBytesLen+ , allocBytes+ , allocBytesWith+ , asCString+ , asCStringLen+ , asBytes+ , unsafeAsBytes+ , asBytesLen+ , unsafeAsBytesLen+ , showBytes+ ) where++import Control.DeepSeq+import Data.ByteString (ByteString)+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as Char8+import qualified Data.ByteString.Internal as ByteString+import qualified Data.ByteString.Unsafe as ByteString+import Data.Word+import Foreign.C.String hiding (peekCString, peekCStringLen,+ withCString, withCStringLen)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr++{-+Small rant: CString is a bit of a mess++- CString doesn't work with const+- There is no CBytes+- Doesn't work with ConstPtr+- Different names for different types (peek vs pack, useAs vs with)+ - Data.ByteString+ - packCString :: CString -> IO ByteString+ - useAsCString :: ByteString -> (CString -> IO a) -> IO a+ - Text+ - Foreign.C.String+ - peekCString :: CString -> IO String+ - withCString :: String -> (CString -> IO a) -> IO a+-}++peekCString :: CString -> IO ByteString+peekCString = ByteString.packCString++withCString :: ByteString -> (CString -> IO a) -> IO a+withCString = ByteString.useAsCString++type CBytes = Ptr Word8++withCBytes :: ByteString -> (CBytes -> IO a) -> IO a+withCBytes bs act = ByteString.useAsCStringLen bs (\ (ptr,_) -> act (castPtr ptr))++type CBytesLen = (Ptr Word8, Int)++withCBytesLen :: ByteString -> (CBytesLen -> IO a) -> IO a+withCBytesLen bs act = ByteString.useAsCStringLen bs (\ (ptr,len) -> act (castPtr ptr, len))++-- A cheap knockoff of ByteArray.alloc / allocRet+-- We'll make this safer in the future+-- NOTE: THIS IS NOT LIKE Foriegn.Marshal.Alloc.allocaBytes, though it is close+-- Instead of returning the thing, we always return a bytestring.+-- Also, allocaBytes frees the memory after, but this is a malloc freed on garbage collect.+-- I basically ripped the relevant bits from ByteArray for ease of continuity+allocBytes :: Int -> (Ptr byte -> IO ()) -> IO ByteString+-- allocBytes sz f = snd <$> allocBytesWith sz f+-- NOTE: This is probably better than mallocByteString withForeignPtr+-- Use of mallocByteString without mkDeferredByteString / deferForeignPtrAvailability+-- is possibly a factor in our InsufficientBufferSpaceException issues+-- NOTE: Most of the comments are rendered moot now :) this needs cleanup+allocBytes sz f = ByteString.create sz (f . castPtr)++allocBytesWith :: Int -> (Ptr byte -> IO a) -> IO (a, ByteString)+allocBytesWith sz f+ | sz < 0 = allocBytesWith 0 f+ | otherwise = do+ fptr <- ByteString.mallocByteString sz+ a <- withForeignPtr fptr (f . castPtr)+ -- return (a, ByteString.PS fptr 0 sz)+ -- NOTE: The safety of this function is suspect, may require deepseq+ let bs = ByteString.PS fptr 0 sz+ in bs `deepseq` return (a,bs)++asCString :: ByteString -> (Ptr CChar -> IO a) -> IO a+asCString = ByteString.useAsCString++asCStringLen :: ByteString -> (Ptr CChar -> CSize -> IO a) -> IO a+asCStringLen bs f = ByteString.useAsCStringLen bs (\ (ptr,len) -> f ptr (fromIntegral len))++asBytes :: ByteString -> (Ptr byte -> IO a) -> IO a+asBytes bs f = asBytesLen bs (\ ptr _ -> f ptr)++unsafeAsBytes :: ByteString -> (Ptr byte -> IO a) -> IO a+unsafeAsBytes bs f = unsafeAsBytesLen bs (\ ptr _ -> f ptr)++-- WARNING: This should not be using `useAsCStringLen`+asBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a+asBytesLen bs f = ByteString.useAsCStringLen bs (\ (ptr,len) -> f (castPtr ptr) (fromIntegral len))++unsafeAsBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a+unsafeAsBytesLen bs f = ByteString.unsafeUseAsCStringLen bs (\ (ptr,len) -> f (castPtr ptr) (fromIntegral len))++showBytes :: (Show a) => a -> ByteString+showBytes = Char8.pack . show
+ src/Botan/Low/Internal/String.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings #-}++module Botan.Low.Internal.String (+ (//)+ , (/$)+ ) where++import Data.String (IsString (..))++infixr 6 //+(//) :: (IsString a, Semigroup a) => a -> a -> a+a // b = a <> "/" <> b++infixr 0 /$+(/$) :: (IsString a, Semigroup a) => a -> a -> a+a /$ b = a <> "(" <> b <> ")"
src/Botan/Low/KDF.hs view
@@ -27,58 +27,56 @@ still use different keys in the two contexts. -} -module Botan.Low.KDF-(---- * Key derivation function-- KDFName(..)-, kdf+module Botan.Low.KDF ( --- * KDF algorithms+ -- * Key derivation function -, pattern HKDF-, hkdf-, pattern HKDF_Extract-, hkdf_extract-, pattern HKDF_Expand-, hkdf_expand-, pattern KDF2-, kdf2-, pattern KDF1_18033-, kdf1_18033-, pattern KDF1-, kdf1-, pattern TLS_12_PRF-, tls_12_prf-, pattern X9_42_PRF-, x9_42_prf-, pattern SP800_108_Counter-, sp800_108_counter-, pattern SP800_108_Feedback-, sp800_108_feedback-, pattern SP800_108_Pipeline-, sp800_108_pipeline-, pattern SP800_56A-, sp800_56A-, pattern SP800_56C-, sp800_56C+ KDFName+ , kdf --- * Convenience+ -- * KDF algorithms -, kdfs+ , pattern HKDF+ , hkdf+ , pattern HKDF_Extract+ , hkdf_extract+ , pattern HKDF_Expand+ , hkdf_expand+ , pattern KDF2+ , kdf2+ , pattern KDF1_18033+ , kdf1_18033+ , pattern KDF1+ , kdf1+ , pattern TLS_12_PRF+ , tls_12_prf+ , pattern X9_42_PRF+ , x9_42_prf+ , pattern SP800_108_Counter+ , sp800_108_counter+ , pattern SP800_108_Feedback+ , sp800_108_feedback+ , pattern SP800_108_Pipeline+ , sp800_108_pipeline+ , pattern SP800_56A+ , sp800_56A+ , pattern SP800_56C+ , sp800_56C -) where+ -- * Convenience -import qualified Data.ByteString as ByteString+ , kdfs -import Botan.Bindings.KDF+ ) where -import Botan.Low.Hash-import Botan.Low.MAC-import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.KDF+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.MAC+import Data.ByteString (ByteString) type KDFName = ByteString @@ -113,19 +111,32 @@ hkdf :: HashName -> KDFName hkdf h = HKDF /$ h+hkdf_extract :: KDFName -> KDFName hkdf_extract h = HKDF_Extract /$ h+hkdf_expand :: KDFName -> KDFName hkdf_expand h = HKDF_Expand /$ h+kdf2 :: KDFName -> KDFName kdf2 h = KDF2 /$ h+kdf1_18033 :: KDFName -> KDFName kdf1_18033 h = KDF1_18033 /$ h+kdf1 :: KDFName -> KDFName kdf1 h = KDF1 /$ h+tls_12_prf :: KDFName -> KDFName tls_12_prf h = TLS_12_PRF /$ h+x9_42_prf :: KDFName -> KDFName x9_42_prf h = X9_42_PRF /$ h+sp800_108_counter :: KDFName -> KDFName sp800_108_counter h = SP800_108_Counter /$ HMAC /$ h+sp800_108_feedback :: KDFName -> KDFName sp800_108_feedback h = SP800_108_Feedback /$ HMAC /$ h+sp800_108_pipeline :: KDFName -> KDFName sp800_108_pipeline h = SP800_108_Pipeline /$ HMAC /$ h+sp800_56A :: KDFName -> KDFName sp800_56A h = SP800_56A /$ HMAC /$ h+sp800_56C :: KDFName -> KDFName sp800_56C h = SP800_56C /$ HMAC /$ h +kdfs :: [KDFName] kdfs = concat [ [ hkdf h | h <- cryptohashes ] , [ hkdf_extract h | h <- cryptohashes ]
src/Botan/Low/KeyWrap.hs view
@@ -22,22 +22,19 @@ Use AES for best interop. -} -module Botan.Low.KeyWrap-(-- nistKeyWrapEncode-, nistKeyWrapDecode--) where+module Botan.Low.KeyWrap ( -import qualified Data.ByteString as ByteString+ nistKeyWrapEncode+ , nistKeyWrapDecode -import Botan.Bindings.KeyWrap+ ) where -import Botan.Low.BlockCipher-import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.KeyWrap+import Botan.Low.BlockCipher+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Data.ByteString (ByteString) nistKeyWrapEncode :: BlockCipherName -- ^ __cipher_algo__
src/Botan/Low/MAC.hs view
@@ -35,59 +35,61 @@ - Finalize the MAC computation. -} -module Botan.Low.MAC-(---- * Message authentication codes--- $introduction+{-# LANGUAGE OverloadedStrings #-} --- * Usage--- $usage+module Botan.Low.MAC ( - MAC(..)-, MACName(..)-, MACKey(..)-, MACNonce(..)-, MACDigest(..)-, withMAC-, macInit-, macDestroy-, macName-, macOutputLength-, macGetKeyspec-, macSetKey-, macSetNonce-, macUpdate-, macFinal-, macClear+ -- * Message authentication codes+ -- $introduction --- * MAC algorithms+ -- * Usage+ -- $usage -, pattern CMAC-, cmac-, pattern GMAC-, gmac-, pattern HMAC-, hmac-, pattern Poly1305-, pattern SipHash-, sipHash-, pattern X9_19_MAC+ MAC(..)+ , MACName+ , MACKey+ , MACNonce+ , MACDigest+ , withMAC+ , macInit+ , macDestroy+ , macName+ , macOutputLength+ , macGetKeyspec+ , macSetKey+ , macSetNonce+ , macUpdate+ , macFinal+ , macClear --- * Convenience+ -- * MAC algorithms -) where+ , pattern CMAC+ , cmac+ , pattern GMAC+ , gmac+ , pattern HMAC+ , hmac+ , pattern Poly1305+ , pattern SipHash+ , sipHash+ , pattern X9_19_MAC -import qualified Data.ByteString as ByteString+ -- * Convenience -import Botan.Bindings.MAC+ ) where -import Botan.Low.BlockCipher-import Botan.Low.Error-import Botan.Low.Hash-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+import Botan.Bindings.MAC+import Botan.Low.BlockCipher+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.Make+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr {- $introduction @@ -162,14 +164,13 @@ newtype MAC = MkMAC { getMACForeignPtr :: ForeignPtr BotanMACStruct } -newMAC :: BotanMAC -> IO MAC withMAC :: MAC -> (BotanMAC -> IO a) -> IO a macDestroy :: MAC -> IO () createMAC :: (Ptr BotanMAC -> IO CInt) -> IO MAC-(newMAC, withMAC, macDestroy, createMAC, _)+(withMAC, macDestroy, createMAC) = mkBindings- MkBotanMAC runBotanMAC- MkMAC getMACForeignPtr+ MkBotanMAC (.runBotanMAC)+ MkMAC (.getMACForeignPtr) botan_mac_destroy type MACName = ByteString@@ -228,10 +229,6 @@ :: MACName -- ^ __mac_name__: name of the hash function, e.g., "HMAC(SHA-384)" -> IO MAC -- ^ __mac__: mac object macInit = mkCreateObjectCString createMAC (\ out name -> botan_mac_init out name 0)---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withMACInit :: MACName -> (MAC -> IO a) -> IO a-withMACInit = mkWithTemp1 macInit macDestroy -- | Writes the output length of the message authentication code to *output_length macOutputLength
src/Botan/Low/MPI.hs view
@@ -9,63 +9,68 @@ Portability : POSIX -} -module Botan.Low.MPI-(+module Botan.Low.MPI ( - MP(..)-, withMP-, mpInit-, mpDestroy-, mpToHex-, mpToStr-, mpClear-, mpSetFromInt-, mpSetFromMP-, mpCopy-, mpSetFromStr-, mpSetFromRadixStr-, mpNumBits-, mpNumBytes-, mpToBin-, mpFromBin-, mpToWord32-, mpIsPositive-, mpIsNegative-, mpFlipSign-, mpIsZero-, mpAddWord32-, mpSubWord32-, mpAdd-, mpSub-, mpMul-, mpDiv-, mpModMul-, mpEqual-, mpCmp-, mpSwap-, mpPowMod-, mpLeftShift-, mpRightShift-, mpModInverse-, mpRandBits-, mpRandRange-, mpGCD-, mpIsPrime-, mpGetBit-, mpSetBit-, mpClearBit+ MP(..)+ , withMP+ , mpInit+ , mpDestroy+ , mpToHex+ , mpToStr+ , mpClear+ , mpSetFromInt+ , mpSetFromMP+ , mpCopy+ , mpSetFromStr+ , mpSetFromRadixStr+ , mpNumBits+ , mpNumBytes+ , mpToBin+ , mpFromBin+ , mpToWord32+ , mpIsPositive+ , mpIsNegative+ , mpFlipSign+ , mpIsZero+ , mpAddWord32+ , mpSubWord32+ , mpAdd+ , mpSub+ , mpMul+ , mpDiv+ , mpModMul+ , mpEqual+ , mpCmp+ , mpSwap+ , mpPowMod+ , mpLeftShift+ , mpRightShift+ , mpModInverse+ , mpRandBits+ , mpRandRange+ , mpGCD+ , mpIsPrime+ , mpGetBit+ , mpSetBit+ , mpClearBit -) where+ ) where +import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.MPI+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Botan.Low.Remake+import Botan.Low.RNG+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Bindings.MPI--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake-import Botan.Low.RNG+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- Yes, the module is named MPI, but the type is MP. -- I'm probably renaming the module / type to `Botan.Integer` for ergonomics,@@ -82,14 +87,13 @@ newtype MP = MkMP { getMPForeignPtr :: ForeignPtr BotanMPStruct } -newMP :: BotanMP -> IO MP withMP :: MP -> (BotanMP -> IO a) -> IO a mpDestroy :: MP -> IO () createMP :: (Ptr BotanMP -> IO CInt) -> IO MP-(newMP, withMP, mpDestroy, createMP, _)+(withMP, mpDestroy, createMP) = mkBindings- MkBotanMP runBotanMP- MkMP getMPForeignPtr+ MkBotanMP (.runBotanMP)+ MkMP (.getMPForeignPtr) botan_mp_destroy mpInit :: IO MP@@ -261,7 +265,7 @@ -- -- int botan_...(botan_mp_t dest, const botan_mp_t source);-type UnaryOp ptr = ptr -> ptr -> IO BotanErrorCode+type UnaryOp ptr = ptr -> ptr -> IO CInt mkUnaryOp :: WithPtr typ ptr -> UnaryOp ptr -> typ -> typ -> IO () mkUnaryOp withPtr unary dest source = withPtr dest $ \ destPtr -> do@@ -269,7 +273,7 @@ throwBotanIfNegative_ $ unary destPtr sourcePtr -- int botan_...(botan_mp_t dest, const botan_mp_t source, size_t factor);-type UnaryOp_csize ptr = ptr -> ptr -> CSize -> IO BotanErrorCode+type UnaryOp_csize ptr = ptr -> ptr -> CSize -> IO CInt mkUnaryOp_csize :: WithPtr typ ptr -> UnaryOp_csize ptr -> typ -> typ -> Int -> IO () mkUnaryOp_csize withPtr unary dest source factor = withPtr dest $ \ destPtr -> do@@ -277,7 +281,7 @@ throwBotanIfNegative_ $ unary destPtr sourcePtr (fromIntegral factor) -- int botan_...(botan_mp_t dest, const botan_mp_t a, const botan_mp_t b);-type BinaryOp ptr = ptr -> ptr -> ptr -> IO BotanErrorCode+type BinaryOp ptr = ptr -> ptr -> ptr -> IO CInt mkBinaryOp :: WithPtr typ ptr -> BinaryOp ptr -> typ -> typ -> typ -> IO () mkBinaryOp withPtr binary dest a b = withPtr dest $ \ destPtr -> do@@ -286,7 +290,7 @@ throwBotanIfNegative_ $ binary destPtr aPtr bPtr -- int botan_...(botan_mp_t a, botan_mp_t b, const botan_mp_t x, const botan_mp_t y);-type BinaryDuplexOp ptr = ptr -> ptr -> ptr -> ptr -> IO BotanErrorCode+type BinaryDuplexOp ptr = ptr -> ptr -> ptr -> ptr -> IO CInt -- NOTE: Do not confuse for mkTrinaryOp mkBinaryDuplexOp :: WithPtr typ ptr -> BinaryDuplexOp ptr -> typ -> typ -> typ -> typ -> IO ()@@ -297,7 +301,7 @@ throwBotanIfNegative_ $ binary aPtr bPtr xPtr yPtr -- int botan_...(botan_mp_t a, botan_mp_t b, const botan_mp_t x, const botan_mp_t y);-type TrinaryOp ptr = ptr -> ptr -> ptr -> ptr -> IO BotanErrorCode+type TrinaryOp ptr = ptr -> ptr -> ptr -> ptr -> IO CInt -- NOTE: Do not confuse for mkBinaryDuplexOp mkTrinaryOp :: WithPtr typ ptr -> TrinaryOp ptr -> typ -> typ -> typ -> typ -> IO ()
src/Botan/Low/Make.hs view
@@ -1,9 +1,80 @@-module Botan.Low.Make where+module Botan.Low.Make (+ WithPtr+ , Constr+ , Initializer+ , Initializer_name+ , Initializer_name_flags+ , Initializer_bytes+ , Initializer_bytes_len+ , Destructor+ , mkInit+ , mkInit_name+ , mkInit_name_flags+ , mkInit_bytes+ , mkInit_bytes_len+ , Initializer_with+ , mkInit_with+ , GetBytes+ , mkGetBytes+ , GetCString+ , mkGetCString+ , GetInt+ , mkGetInt+ , GetSize+ , GetSize_csize+ , GetSizes2+ , GetSizes3+ , mkGetSize+ , mkGetSize_csize+ , mkGetSizes2+ , mkGetSizes3+ , GetBoolCode+ , GetBoolCode_csize+ , mkGetBoolCode+ , mkGetBoolCode_csize+ , GetIntCode+ , GetIntCode_csize+ , mkGetIntCode+ , mkGetIntCode_csize+ , Action+ , mkAction+ , mkSet+ , mkSetOn+ , SetCSize+ , SetCInt+ , mkSetCSize+ , mkSetCInt+ , SetCString+ , SetCString_csize+ , mkSetCString+ , mkSetCString_csize+ , SetBytesLen+ , mkSetBytesLen+ , allocBytesQuerying+ , allocBytesQueryingCString+ , mkWithTemp+ , mkWithTemp1+ , mkWithTemp2+ , mkWithTemp3+ , mkWithTemp4+ , withPtrs+ ) where +import Botan.Bindings.Error+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Control.DeepSeq+import Control.Exception+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Low.Error-import Botan.Low.Prelude+import Data.Word+import Foreign.C.String+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable+import Prelude hiding (init) {- Basic botan type template@@ -45,7 +116,7 @@ {- type Construct struct typ = ForeignPtr struct -> typ type Destruct struct = FinalizerPtr struct-type Initialize0 struct = Ptr (Ptr struct) -> IO BotanErrorCode+type Initialize0 struct = Ptr (Ptr struct) -> IO CInt mkInit0 :: Construct struct typ@@ -93,11 +164,11 @@ type Constr struct typ = ForeignPtr struct -> typ -type Initializer struct = Ptr (Ptr struct) -> IO BotanErrorCode-type Initializer_name struct = Ptr (Ptr struct) -> CString -> IO BotanErrorCode-type Initializer_name_flags struct = Ptr (Ptr struct) -> CString -> Word32 -> IO BotanErrorCode-type Initializer_bytes struct = Ptr (Ptr struct) -> Ptr Word8 -> IO BotanErrorCode-type Initializer_bytes_len struct = Ptr (Ptr struct) -> Ptr Word8 -> CSize -> IO BotanErrorCode+type Initializer struct = Ptr (Ptr struct) -> IO CInt+type Initializer_name struct = Ptr (Ptr struct) -> CString -> IO CInt+type Initializer_name_flags struct = Ptr (Ptr struct) -> CString -> Word32 -> IO CInt+type Initializer_bytes struct = Ptr (Ptr struct) -> Ptr Word8 -> IO CInt+type Initializer_bytes_len struct = Ptr (Ptr struct) -> Ptr Word8 -> CSize -> IO CInt type Destructor struct = FinalizerPtr struct @@ -169,7 +240,7 @@ -- Initializing with another botan object -- TODO: Use this in already-implemented functions as appropriate -type Initializer_with struct withptr = Ptr (Ptr struct) -> withptr -> IO BotanErrorCode+type Initializer_with struct withptr = Ptr (Ptr struct) -> withptr -> IO CInt mkInit_with :: Constr struct typ@@ -188,7 +259,7 @@ Non-effectful queries -} --- type GetName ptr = ptr -> Ptr CChar -> Ptr CSize -> IO BotanErrorCode+-- type GetName ptr = ptr -> Ptr CChar -> Ptr CSize -> IO CInt -- Replaced by the new mkGetCString -- -- TODO: Prefer mkGetBytes / mkGetCString to mkGetName@@ -209,7 +280,7 @@ -- NOTE: This now handles both Ptr Word8 and Ptr CChar -- This reads the entire byte buffer, including any \NUL bytes-type GetBytes ptr byte = ptr -> Ptr byte -> Ptr CSize -> IO BotanErrorCode+type GetBytes ptr byte = ptr -> Ptr byte -> Ptr CSize -> IO CInt mkGetBytes :: WithPtr typ ptr@@ -219,7 +290,7 @@ allocBytesQuerying $ \ outPtr outLen -> get typPtr outPtr outLen -- NOTE This reads a CString, up to the first \NUL-type GetCString ptr byte = ptr -> Ptr byte -> Ptr CSize -> IO BotanErrorCode+type GetCString ptr byte = ptr -> Ptr byte -> Ptr CSize -> IO CInt mkGetCString :: WithPtr typ ptr@@ -228,7 +299,7 @@ mkGetCString withPtr get typ = withPtr typ $ \ typPtr -> do allocBytesQueryingCString $ \ outPtr outLen -> get typPtr outPtr outLen -type GetInt ptr = ptr -> Ptr CInt -> IO BotanErrorCode+type GetInt ptr = ptr -> Ptr CInt -> IO CInt mkGetInt :: WithPtr typ ptr@@ -239,10 +310,10 @@ throwBotanIfNegative_ $ get typPtr szPtr fromIntegral <$> peek szPtr -type GetSize ptr = ptr -> Ptr CSize -> IO BotanErrorCode-type GetSize_csize ptr = ptr -> CSize -> Ptr CSize -> IO BotanErrorCode-type GetSizes2 ptr = ptr -> Ptr CSize -> Ptr CSize -> IO BotanErrorCode-type GetSizes3 ptr = ptr -> Ptr CSize -> Ptr CSize -> Ptr CSize -> IO BotanErrorCode+type GetSize ptr = ptr -> Ptr CSize -> IO CInt+type GetSize_csize ptr = ptr -> CSize -> Ptr CSize -> IO CInt+type GetSizes2 ptr = ptr -> Ptr CSize -> Ptr CSize -> IO CInt+type GetSizes3 ptr = ptr -> Ptr CSize -> Ptr CSize -> Ptr CSize -> IO CInt mkGetSize :: WithPtr typ ptr@@ -285,33 +356,14 @@ szC <- fromIntegral <$> peek szPtrC return (szA,szB,szC) --- type GetBytes ptr = ptr -> Ptr Word8 -> CSize -> IO BotanErrorCode+-- type GetBytes ptr = ptr -> Ptr Word8 -> CSize -> IO CInt -- NOTE: Get...Code nomenclature signifies that we get the desired return value -- from the error code error code, eg they use something other than throwBotanIfNegative_ -- --type GetSuccessCode ptr = ptr -> IO BotanErrorCode-type GetSuccessCode_csize ptr = ptr -> CSize -> IO BotanErrorCode--mkGetSuccessCode- :: WithPtr typ ptr- -> GetSuccessCode ptr- -> typ -> IO Bool-mkGetSuccessCode withPtr get typ = withPtr typ $ \ typPtr -> do- throwBotanCatchingSuccess $ get typPtr--mkGetSuccessCode_csize- :: WithPtr typ ptr- -> GetSuccessCode_csize ptr- -> typ -> Int -> IO Bool-mkGetSuccessCode_csize withPtr get typ sz = withPtr typ $ \ typPtr -> do- throwBotanCatchingSuccess $ get typPtr (fromIntegral sz)---type GetBoolCode ptr = ptr -> IO BotanErrorCode-type GetBoolCode_csize ptr = ptr -> CSize -> IO BotanErrorCode+type GetBoolCode ptr = ptr -> IO CInt+type GetBoolCode_csize ptr = ptr -> CSize -> IO CInt mkGetBoolCode :: WithPtr typ ptr@@ -327,28 +379,28 @@ mkGetBoolCode_csize withPtr get typ sz = withPtr typ $ \ typPtr -> do throwBotanCatchingBool $ get typPtr (fromIntegral sz) -type GetIntCode ptr = ptr -> IO BotanErrorCode-type GetIntCode_csize ptr = ptr -> CSize -> IO BotanErrorCode+type GetIntCode ptr = ptr -> IO CInt+type GetIntCode_csize ptr = ptr -> CSize -> IO CInt -mkGetIntCode- :: WithPtr typ ptr- -> GetIntCode ptr- -> typ -> IO Int+mkGetIntCode ::+ WithPtr typ ptr+ -> GetIntCode ptr+ -> typ -> IO Int mkGetIntCode withPtr get typ = withPtr typ $ \ typPtr -> do- throwBotanCatchingInt $ get typPtr+ fmap fromIntegral $ throwBotanIfNegative $ get typPtr -mkGetIntCode_csize- :: WithPtr typ ptr- -> GetIntCode_csize ptr- -> typ -> CSize -> IO Int+mkGetIntCode_csize ::+ WithPtr typ ptr+ -> GetIntCode_csize ptr+ -> typ -> CSize -> IO Int mkGetIntCode_csize withPtr get typ sz = withPtr typ $ \ typPtr -> do- throwBotanCatchingInt $ get typPtr sz+ fmap fromIntegral $ throwBotanIfNegative $ get typPtr sz {- Effectful actions -} -type Action ptr = ptr -> IO BotanErrorCode+type Action ptr = ptr -> IO CInt mkAction :: WithPtr typ ptr -> Action ptr@@ -358,7 +410,7 @@ mkSet :: WithPtr typ ptr- -> (ptr -> a -> IO BotanErrorCode)+ -> (ptr -> a -> IO CInt) -> typ -> a -> IO () mkSet withPtr set typ a = withPtr typ $ \ typPtr -> do throwBotanIfNegative_ $ set typPtr a@@ -366,13 +418,13 @@ mkSetOn :: WithPtr typ ptr -> (a -> b)- -> (ptr -> b -> IO BotanErrorCode)+ -> (ptr -> b -> IO CInt) -> typ -> a -> IO () mkSetOn withPtr fn set typ sz = withPtr typ $ \ typPtr -> do throwBotanIfNegative_ $ set typPtr (fn sz) -type SetCSize ptr = ptr -> CSize -> IO BotanErrorCode-type SetCInt ptr = ptr -> CInt -> IO BotanErrorCode+type SetCSize ptr = ptr -> CSize -> IO CInt+type SetCInt ptr = ptr -> CInt -> IO CInt mkSetCSize :: WithPtr typ ptr@@ -388,8 +440,8 @@ mkSetCInt withPtr set typ sz = withPtr typ $ \ typPtr -> do throwBotanIfNegative_ $ set typPtr (fromIntegral sz) -type SetCString ptr = ptr -> CString -> IO BotanErrorCode-type SetCString_csize ptr = ptr -> CString -> CSize -> IO BotanErrorCode+type SetCString ptr = ptr -> CString -> IO CInt+type SetCString_csize ptr = ptr -> CString -> CSize -> IO CInt mkSetCString :: WithPtr typ ptr@@ -407,7 +459,7 @@ asCString cstring $ \ cstringPtr -> do throwBotanIfNegative_ $ set typPtr cstringPtr (fromIntegral sz) -type SetBytesLen ptr = ptr -> Ptr Word8 -> CSize -> IO BotanErrorCode+type SetBytesLen ptr = ptr -> Ptr Word8 -> CSize -> IO CInt mkSetBytesLen :: WithPtr typ ptr@@ -423,20 +475,20 @@ -- NOTE: This properly takes advantage of szPtr, queries the buffer size - use this elsewhere -- NOTE: This throws any botan codes other than BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE-allocBytesQuerying :: (Ptr byte -> Ptr CSize -> IO BotanErrorCode) -> IO ByteString+allocBytesQuerying :: (Ptr byte -> Ptr CSize -> IO CInt) -> IO ByteString allocBytesQuerying fn = do alloca $ \ szPtr -> do- -- TODO: Maybe poke szPtr 0 for extra safety in cas its not initially zero+ poke szPtr 0 code <- fn nullPtr szPtr case code of- InsufficientBufferSpace -> do+ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE -> do sz <- fromIntegral <$> peek szPtr allocBytes sz $ \ outPtr -> throwBotanIfNegative_ $ fn outPtr szPtr _ -> do throwBotanError code -- NOTE: Does not check length of taken string, vulnerable to null byte injection-allocBytesQueryingCString :: (Ptr byte -> Ptr CSize -> IO BotanErrorCode) -> IO ByteString+allocBytesQueryingCString :: (Ptr byte -> Ptr CSize -> IO CInt) -> IO ByteString allocBytesQueryingCString action = do cstring <- allocBytesQuerying action return $!! ByteString.takeWhile (/= 0) cstring@@ -463,8 +515,8 @@ -- withPtrs :: (forall a . typ -> (ptr -> IO a) -> IO a) -> [typ] -> ([ptr] -> IO b) -> IO b-withPtrs withPtr [] act = act []-withPtrs withPtr (typ:typs) act = withPtr typ $ \ typPtr -> withPtrs withPtr typs (act . (typPtr:))+withPtrs _withPtr [] act = act []+withPtrs withPtr (typ:typs) act = withPtr typ $ \ typPtr -> withPtrs withPtr typs (act . (typPtr:)) -- withNullablePtr
− src/Botan/Low/Prelude.hs
@@ -1,247 +0,0 @@-module Botan.Low.Prelude-( module Prelude-, module Control.Monad-, module Control.Exception-, module Control.DeepSeq-, module Data.ByteString-, module Data.String-, module Data.Text-, module Data.Word-, module System.IO-, module Foreign.C.String-, module Foreign.C.Types-, module Foreign.ForeignPtr-, module Foreign.Marshal.Alloc-, module Foreign.Marshal.Array-, module Foreign.Ptr-, module Foreign.Storable-, module GHC.Stack-, ConstPtr(..)-, peekCString-, withCString-, withCBytes-, withCBytesLen-, withConstCString-, withMany--- Old-, peekCStringText-, allocBytes-, allocBytesWith-, asCString-, asCStringLen-, asBytes-, unsafeAsBytes-, asBytesLen-, unsafeAsBytesLen--- Helpers-, (//)-, (/$)-, showBytes-) where---- Re-exported modules--import Prelude--import Control.Monad-import Control.Exception-import Control.DeepSeq--import Data.ByteString (ByteString)-import Data.String (IsString(..))-import Data.Text (Text)--import Data.Word--import System.IO--import Foreign.C.String hiding (peekCString, peekCStringLen, withCString, withCStringLen)-import Foreign.C.Types-import Foreign.ForeignPtr-import Foreign.Marshal.Alloc-import Foreign.Marshal.Array-import Foreign.Ptr-import Foreign.Storable--import GHC.Stack---- Other Imports--import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Internal as ByteString-import qualified Data.ByteString.Unsafe as ByteString--import qualified Data.ByteString.Char8 as Char8--import qualified Data.Text.Encoding as Text--import Botan.Bindings.Prelude (ConstPtr(..))--{--Small rant: CString is a bit of a mess--- CString doesn't work with const-- There is no CBytes-- Doesn't work with ConstPtr-- Different names for different types (peek vs pack, useAs vs with)- - Data.ByteString- - packCString :: CString -> IO ByteString- - useAsCString :: ByteString -> (CString -> IO a) -> IO a- - Text- - Foreign.C.String- - peekCString :: CString -> IO String- - withCString :: String -> (CString -> IO a) -> IO a--}--{--BETTER VERSIONS--}---- Types---- Safe functions that make a temporary copy--- Only care about ByteString, leave Text for higher-level botan---- type CString = Ptr CChar--peekCString :: CString -> IO ByteString-peekCString = ByteString.packCString---- Replaces 'asCString'-withCString :: ByteString -> (CString -> IO a) -> IO a-withCString = ByteString.useAsCString---- type CStringLen = (Ptr CChar, Int)--peekCStringLen :: CStringLen -> IO ByteString-peekCStringLen = ByteString.packCStringLen---- Replaces 'asCStringLen'-withCStringLen :: ByteString -> (CStringLen -> IO a) -> IO a-withCStringLen = ByteString.useAsCStringLen--type CBytes = Ptr Word8---- peekCBytes :: CBytes -> Int -> IO ByteString--- peekCBytes = undefined--withCBytes :: ByteString -> (CBytes -> IO a) -> IO a-withCBytes bs act = ByteString.useAsCStringLen bs (\ (ptr,_) -> act (castPtr ptr))--type CBytesLen = (Ptr Word8, Int)--peekCBytesLen :: CBytesLen -> IO ByteString-peekCBytesLen (ptr, len) = ByteString.packCStringLen (castPtr ptr, len)---- Replaces 'asBytesLen'-withCBytesLen :: ByteString -> (CBytesLen -> IO a) -> IO a-withCBytesLen bs act = ByteString.useAsCStringLen bs (\ (ptr,len) -> act (castPtr ptr, len))---- QUESTION: Is it worth it to have extra types for ConstPtr versions?--{--type ConstCString = ConstPtr CChar-type ConstCStringLen = (ConstPtr CChar, Int)--type ConstCBytes = ConstPtr Word8-type ConstCBytesLen = (ConstPtr Word8, Int)--}---- TODO: Replace--- withCString str $ \ cstr -> ... (ConstPtr cstr) ...--- with--- withConstCString str $ \ cstr -> ... cstr ...-withConstCString :: ByteString -> (ConstPtr CChar -> IO a) -> IO a-withConstCString bs act = ByteString.useAsCString bs (act . ConstPtr)--{--Misc--}--withMany- :: (forall a . object -> (cobject -> IO a) -> IO a)- -> [object]- -> ([cobject] -> IO b)- -> IO b-withMany withObject [] act = act []-withMany withObject (obj:objs) act = withObject obj $ \ cobj -> withMany withObject objs (act . (cobj:))--{--OLD--}----- Because:--- https://github.com/haskell/text/issues/239--- Is still an issue-peekCStringText :: CString -> IO Text-peekCStringText cs = do- bs <- ByteString.unsafePackCString cs- return $! Text.decodeUtf8 bs---- A cheap knockoff of ByteArray.alloc / allocRet--- We'll make this safer in the future--- NOTE: THIS IS NOT LIKE Foriegn.Marshal.Alloc.allocaBytes, though it is close--- Instead of returning the thing, we always return a bytestring.--- Also, allocaBytes frees the memory after, but this is a malloc freed on garbage collect.--- I basically ripped the relevant bits from ByteArray for ease of continuity-allocBytes :: Int -> (Ptr byte -> IO ()) -> IO ByteString--- allocBytes sz f = snd <$> allocBytesWith sz f--- NOTE: This is probably better than mallocByteString withForeignPtr--- Use of mallocByteString without mkDeferredByteString / deferForeignPtrAvailability--- is possibly a factor in our InsufficientBufferSpaceException issues--- NOTE: Most of the comments are rendered moot now :) this needs cleanup-allocBytes sz f = ByteString.create sz (f . castPtr)--allocBytesWith :: Int -> (Ptr byte -> IO a) -> IO (a, ByteString)-allocBytesWith sz f- | sz < 0 = allocBytesWith 0 f- | otherwise = do- fptr <- ByteString.mallocByteString sz- a <- withForeignPtr fptr (f . castPtr)- -- return (a, ByteString.PS fptr 0 sz)- -- NOTE: The safety of this function is suspect, may require deepseq- let bs = ByteString.PS fptr 0 sz- in bs `deepseq` return (a,bs)---- ByteString.create' doesn't exist--- TODO: Replace allocBytesWith with this-createByteString' :: Int -> (Ptr byte -> IO a) -> IO (ByteString,a)-createByteString' sz action = ByteString.createUptoN' sz $ \ ptr -> do- a <- action (castPtr ptr)- return (sz,a)-{-# INLINE createByteString' #-}------asCString :: ByteString -> (Ptr CChar -> IO a) -> IO a-asCString = ByteString.useAsCString--asCStringLen :: ByteString -> (Ptr CChar -> CSize -> IO a) -> IO a-asCStringLen bs f = ByteString.useAsCStringLen bs (\ (ptr,len) -> f ptr (fromIntegral len))--asBytes :: ByteString -> (Ptr byte -> IO a) -> IO a-asBytes bs f = asBytesLen bs (\ ptr _ -> f ptr)--unsafeAsBytes :: ByteString -> (Ptr byte -> IO a) -> IO a-unsafeAsBytes bs f = unsafeAsBytesLen bs (\ ptr _ -> f ptr)---- WARNING: This should not be using `useAsCStringLen`-asBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a-asBytesLen bs f = ByteString.useAsCStringLen bs (\ (ptr,len) -> f (castPtr ptr) (fromIntegral len))--unsafeAsBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a-unsafeAsBytesLen bs f = ByteString.unsafeUseAsCStringLen bs (\ (ptr,len) -> f (castPtr ptr) (fromIntegral len))---- Helpers used in a few name constructors--infixr 6 //-(//) :: (IsString a, Semigroup a) => a -> a -> a-a // b = a <> "/" <> b--infixr 0 /$-(/$) :: (IsString a, Semigroup a) => a -> a -> a-a /$ b = a <> "(" <> b <> ")"--showBytes :: (Show a) => a -> ByteString-showBytes = Char8.pack . show
src/Botan/Low/PubKey.hs view
@@ -12,187 +12,192 @@ for encryption, signatures, and key agreement. -} -module Botan.Low.PubKey-(---- * Private keys- PrivKey(..)-, CheckKeyFlags(..)-, pattern CheckKeyNormalTests-, pattern CheckKeyExpensiveTests-, PrivKeyExportFlags(..)-, pattern PrivKeyExportDER-, pattern PrivKeyExportPEM-, withPrivKey-, privKeyCreate-, privKeyLoad-, privKeyDestroy-, privKeyAlgoName-, privKeyCheckKey-, privKeyGetField-, privKeyExport-, privKeyExportPubKey+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-} --- * Public Keys+module Botan.Low.PubKey ( -, PubKey(..)-, withPubKey-, pubKeyLoad-, pubKeyDestroy-, pubKeyAlgoName-, pubKeyCheckKey-, pubKeyEstimatedStrength-, pubKeyFingerprint-, pubKeyGetField-, pubKeyExport+ -- * Private keys+ PrivKey(..)+ , CheckKeyFlags(..)+ , checkKeyFlags+ , PrivKeyExportFlags(..)+ , privKeyExportFlags+ , withPrivKey+ , privKeyCreate+ , privKeyLoad+ , privKeyDestroy+ , privKeyAlgoName+ , privKeyCheckKey+ , privKeyGetField+ , privKeyExport+ , privKeyExportPubKey --- * PK Algorithms+ -- * Public Keys -, PKName(..)-, pattern RSA-, pattern SM2-, pattern ElGamal-, pattern DSA-, pattern ECDSA-, pattern ECKCDSA-, pattern ECGDSA-, pattern GOST_34_10-, pattern Ed25519-, pattern XMSS-, pattern DH-, pattern ECDH-, pattern Curve25519-, pattern Dilithium-, pattern Kyber-, pattern McEliece+ , PubKey(..)+ , withPubKey+ , pubKeyLoad+ , pubKeyDestroy+ , pubKeyAlgoName+ , pubKeyCheckKey+ , pubKeyEstimatedStrength+ , pubKeyFingerprint+ , pubKeyGetField+ , pubKeyExport --- ** DLGroup+ -- * PK Algorithms -, DLGroupName(..)-, pattern FFDHE_IETF_2048-, pattern FFDHE_IETF_3072-, pattern FFDHE_IETF_4096-, pattern FFDHE_IETF_6144-, pattern FFDHE_IETF_8192-, pattern MODP_IETF_1024-, pattern MODP_IETF_1536-, pattern MODP_IETF_2048-, pattern MODP_IETF_3072-, pattern MODP_IETF_4096-, pattern MODP_IETF_6144-, pattern MODP_IETF_8192-, pattern MODP_SRP_1024-, pattern MODP_SRP_1536-, pattern MODP_SRP_2048-, pattern MODP_SRP_3072-, pattern MODP_SRP_4096-, pattern MODP_SRP_6144-, pattern MODP_SRP_8192-, pattern DSA_JCE_1024-, pattern DSA_BOTAN_2048-, pattern DSA_BOTAN_3072+ , PKName+ , pattern RSA+ , pattern SM2+ , pattern ElGamal+ , pattern DSA+ , pattern ECDSA+ , pattern ECKCDSA+ , pattern ECGDSA+ , pattern GOST_34_10+ , pattern Ed25519+ , pattern XMSS+ , pattern DH+ , pattern ECDH+ , pattern Curve25519+ , pattern Dilithium+ , pattern Kyber+ , pattern McEliece --- ** ECGroup+ -- ** DLGroup -, ECGroupName(..)-, pattern Secp160k1-, pattern Secp160r1-, pattern Secp160r2-, pattern Secp192k1-, pattern Secp192r1-, pattern Secp224k1-, pattern Secp224r1-, pattern Secp256k1-, pattern Secp256r1-, pattern Secp384r1-, pattern Secp521r1-, pattern Brainpool160r1-, pattern Brainpool192r1-, pattern Brainpool224r1-, pattern Brainpool256r1-, pattern Brainpool320r1-, pattern Brainpool384r1-, pattern Brainpool512r1-, pattern X962_p192v2-, pattern X962_p192v3-, pattern X962_p239v1-, pattern X962_p239v2-, pattern X962_p239v3-, pattern Gost_256A-, pattern Gost_512A-, pattern Frp256v1-, pattern Sm2p256v1+ , DLGroupName+ , pattern FFDHE_IETF_2048+ , pattern FFDHE_IETF_3072+ , pattern FFDHE_IETF_4096+ , pattern FFDHE_IETF_6144+ , pattern FFDHE_IETF_8192+ , pattern MODP_IETF_1024+ , pattern MODP_IETF_1536+ , pattern MODP_IETF_2048+ , pattern MODP_IETF_3072+ , pattern MODP_IETF_4096+ , pattern MODP_IETF_6144+ , pattern MODP_IETF_8192+ , pattern MODP_SRP_1024+ , pattern MODP_SRP_1536+ , pattern MODP_SRP_2048+ , pattern MODP_SRP_3072+ , pattern MODP_SRP_4096+ , pattern MODP_SRP_6144+ , pattern MODP_SRP_8192+ , pattern DSA_JCE_1024+ , pattern DSA_BOTAN_2048+ , pattern DSA_BOTAN_3072 --- ** XMSS+ -- ** ECGroup -, XMSSName(..)-, pattern XMSS_SHA2_10_256-, pattern XMSS_SHA2_16_256-, pattern XMSS_SHA2_20_256-, pattern XMSS_SHA2_10_512-, pattern XMSS_SHA2_16_512-, pattern XMSS_SHA2_20_512-, pattern XMSS_SHAKE_10_256-, pattern XMSS_SHAKE_16_256-, pattern XMSS_SHAKE_20_256-, pattern XMSS_SHAKE_10_512-, pattern XMSS_SHAKE_16_512-, pattern XMSS_SHAKE_20_512+ , ECGroupName+ , pattern Secp160k1+ , pattern Secp160r1+ , pattern Secp160r2+ , pattern Secp192k1+ , pattern Secp192r1+ , pattern Secp224k1+ , pattern Secp224r1+ , pattern Secp256k1+ , pattern Secp256r1+ , pattern Secp384r1+ , pattern Secp521r1+ , pattern Brainpool160r1+ , pattern Brainpool192r1+ , pattern Brainpool224r1+ , pattern Brainpool256r1+ , pattern Brainpool320r1+ , pattern Brainpool384r1+ , pattern Brainpool512r1+ , pattern X962_p192v2+ , pattern X962_p192v3+ , pattern X962_p239v1+ , pattern X962_p239v2+ , pattern X962_p239v3+ , pattern Gost_256A+ , pattern Gost_512A+ , pattern Frp256v1+ , pattern Sm2p256v1 --- * EME+ -- ** XMSS -, EMEName(..)-, pattern EME_RAW-, pattern EME_PKCS1_v1_5-, pattern EME_OAEP-, eme_raw-, eme_pkcs1_v1_5-, eme_oaep--- , eme_oaep_mgf-, eme_hash-, eme_sm2EncParam+ , XMSSName+ , pattern XMSS_SHA2_10_256+ , pattern XMSS_SHA2_16_256+ , pattern XMSS_SHA2_20_256+ , pattern XMSS_SHA2_10_512+ , pattern XMSS_SHA2_16_512+ , pattern XMSS_SHA2_20_512+ , pattern XMSS_SHAKE_10_256+ , pattern XMSS_SHAKE_16_256+ , pattern XMSS_SHAKE_20_256+ , pattern XMSS_SHAKE_10_512+ , pattern XMSS_SHAKE_16_512+ , pattern XMSS_SHAKE_20_512 --- * EMSA+ -- * EME -, EMSAName(..)-, emsa_none-, emsa_emsa4-, emsa_hash-, emsa_ed25519Pure-, emsa_ed25519Prehashed-, emsa_ed25519GnuPG-, emsa_sm2SignParam+ , EMEName+ , pattern EME_RAW+ , pattern EME_PKCS1_v1_5+ , pattern EME_OAEP+ , eme_raw+ , eme_pkcs1_v1_5+ , eme_oaep+ -- , eme_oaep_mgf+ , eme_hash+ , eme_sm2EncParam --- * Convenience+ -- * EMSA --- , PKPaddingName(..)-, createPrivKey-, createPubKey-, mkPrivKeyLoad1_name-, mkPrivKeyLoad3-, mkPrivKeyLoad4-, mkPubKeyLoad2-, mkPubKeyLoad2_name-, mkPubKeyLoad3-, mkPubKeyLoad4+ , EMSAName+ , emsa_none+ , emsa_emsa4+ , emsa_hash+ , emsa_ed25519Pure+ , emsa_ed25519Prehashed+ , emsa_ed25519GnuPG+ , emsa_sm2SignParam -) where+ -- * Convenience -import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Unsafe as ByteString+ -- , PKPaddingName(..)+ , createPrivKey+ , createPubKey+ , mkPrivKeyLoad1_name+ , mkPrivKeyLoad3+ , mkPrivKeyLoad4+ , mkPubKeyLoad2+ , mkPubKeyLoad2_name+ , mkPubKeyLoad3+ , mkPubKeyLoad4 -import Botan.Bindings.MPI-import Botan.Bindings.PubKey-import Botan.Bindings.RNG+ ) where -import Botan.Low.Error-import Botan.Low.Hash-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.Remake-import Botan.Low.RNG-import Botan.Low.View+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.MPI+import Botan.Bindings.PubKey+import Botan.Bindings.RNG+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Botan.Low.Make+import Botan.Low.MPI+import Botan.Low.Remake+import Botan.Low.RNG+import Data.ByteString (ByteString)+import qualified Data.ByteString as ByteString+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable {- $introduction @@ -272,26 +277,21 @@ -- Associated types -type PKPaddingName = ByteString- -- /* -- * Public/private key creation, import, ... -- */ newtype PrivKey = MkPrivKey { getPrivKeyForeignPtr :: ForeignPtr BotanPrivKeyStruct } -newPrivKey :: BotanPrivKey -> IO PrivKey withPrivKey :: PrivKey -> (BotanPrivKey -> IO a) -> IO a privKeyDestroy :: PrivKey -> IO () createPrivKey :: (Ptr BotanPrivKey -> IO CInt) -> IO PrivKey-(newPrivKey, withPrivKey, privKeyDestroy, createPrivKey, _)+(withPrivKey, privKeyDestroy, createPrivKey) = mkBindings- MkBotanPrivKey runBotanPrivKey- MkPrivKey getPrivKeyForeignPtr+ MkBotanPrivKey (.runBotanPrivKey)+ MkPrivKey (.getPrivKeyForeignPtr) botan_privkey_destroy -type PrivKeyName = ByteString- type PKName = ByteString pattern RSA@@ -503,16 +503,7 @@ eme_sm2EncParam :: HashName -> EMEName eme_sm2EncParam h = h -type MGFName = ByteString -pattern MGF1- :: MGFName--pattern MGF1 = BOTAN_MGF_MGF1--mgf1 :: HashName -> MGFName-mgf1 h = MGF1 /$ h- {- | Encoding Method for Signature with Appendix @@ -562,17 +553,13 @@ (ConstPtr paramsPtr) botanRNG --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withPrivKeyCreate :: ByteString -> ByteString -> RNG -> (PrivKey -> IO a) -> IO a-withPrivKeyCreate = mkWithTemp3 privKeyCreate privKeyDestroy--type CheckKeyFlags = Word32+data CheckKeyFlags =+ CheckKeyNormalTests+ | CheckKeyExpensiveTests -pattern CheckKeyNormalTests- , CheckKeyExpensiveTests- :: CheckKeyFlags-pattern CheckKeyNormalTests = BOTAN_CHECK_KEY_NORMAL_TESTS-pattern CheckKeyExpensiveTests = BOTAN_CHECK_KEY_EXPENSIVE_TESTS+checkKeyFlags :: CheckKeyFlags -> Word32+checkKeyFlags CheckKeyNormalTests = BOTAN_CHECK_KEY_NORMAL_TESTS+checkKeyFlags CheckKeyExpensiveTests = BOTAN_CHECK_KEY_EXPENSIVE_TESTS -- TODO: Probably catch -1 (INVALID_INPUT), return Bool -- | Check the validity of a private key@@ -583,7 +570,7 @@ -> IO () privKeyCheckKey sk rng flags = withPrivKey sk $ \ skPtr -> do withRNG rng $ \ botanRNG -> do- throwBotanIfNegative_ $ botan_privkey_check_key skPtr botanRNG flags+ throwBotanIfNegative_ $ botan_privkey_check_key skPtr botanRNG (checkKeyFlags flags) -- NOTE: Expectes PKCS #8 / PEM structure -- botan_privkey_export -> null password? and botan_privkey_export_encrypted_... -> use a password?@@ -611,14 +598,13 @@ bitsLen (ConstPtr passwordPtr) -type PrivKeyExportFlags = Word32--pattern PrivKeyExportDER- , PrivKeyExportPEM- :: PrivKeyExportFlags+data PrivKeyExportFlags =+ PrivKeyExportDER+ | PrivKeyExportPEM -pattern PrivKeyExportDER = BOTAN_PRIVKEY_EXPORT_FLAG_DER-pattern PrivKeyExportPEM = BOTAN_PRIVKEY_EXPORT_FLAG_PEM+privKeyExportFlags :: PrivKeyExportFlags -> Word32+privKeyExportFlags PrivKeyExportDER = BOTAN_PRIVKEY_EXPORT_FLAG_DER+privKeyExportFlags PrivKeyExportPEM = BOTAN_PRIVKEY_EXPORT_FLAG_PEM -- NOTE: Different from allocBytesQuerying / INSUFFICIENT_BUFFER_SPACE {- |@@ -636,13 +622,13 @@ alloca $ \szPtr -> do poke szPtr 0 -- NOTE: Presumed be -1- _ <- botan_privkey_export skPtr nullPtr szPtr flags+ _ <- botan_privkey_export skPtr nullPtr szPtr (privKeyExportFlags flags) sz <- peek szPtr allocBytes (fromIntegral sz) $ \ bytesPtr -> do- throwBotanIfNegative_ $ botan_privkey_export skPtr bytesPtr szPtr flags+ throwBotanIfNegative_ $ botan_privkey_export skPtr bytesPtr szPtr (privKeyExportFlags flags) -- TODO:--- | View the private key's DER encoding+-- View the private key's DER encoding -- privKeyViewDER -- :: BotanPrivKey -- ^ __key__ -- -> BotanViewContext ctx -- ^ __ctx__@@ -650,7 +636,7 @@ -- -> IO CInt -- TODO:--- | View the private key's PEM encoding+-- View the private key's PEM encoding -- privKeyViewPEM -- :: BotanPrivKey -- ^ __key__ -- -> BotanViewContext ctx -- ^ __ctx__@@ -688,27 +674,20 @@ newtype PubKey = MkPubKey { getPubKeyForeignPtr :: ForeignPtr BotanPubKeyStruct } -newPubKey :: BotanPubKey -> IO PubKey withPubKey :: PubKey -> (BotanPubKey -> IO a) -> IO a pubKeyDestroy :: PubKey -> IO () createPubKey :: (Ptr BotanPubKey -> IO CInt) -> IO PubKey-(newPubKey, withPubKey, pubKeyDestroy, createPubKey, _)+(withPubKey, pubKeyDestroy, createPubKey) = mkBindings- MkBotanPubKey runBotanPubKey- MkPubKey getPubKeyForeignPtr+ MkBotanPubKey (.runBotanPubKey)+ MkPubKey (.getPubKeyForeignPtr) botan_pubkey_destroy -type PubKeyName = ByteString- pubKeyLoad :: ByteString -- ^ __bits[]__ -> IO PubKey -- ^ __key__ pubKeyLoad = mkCreateObjectCBytesLen createPubKey botan_pubkey_load --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withPubKeyLoad :: ByteString -> (PubKey -> IO a) -> IO a-withPubKeyLoad = mkWithTemp1 pubKeyLoad pubKeyDestroy- privKeyExportPubKey :: PrivKey -- ^ __in__ -> IO PubKey -- ^ __out__@@ -716,13 +695,7 @@ type PubKeyExportFlags = PrivKeyExportFlags -pattern PubKeyExportDER- , PubKeyExportPEM- :: PubKeyExportFlags -pattern PubKeyExportDER = PrivKeyExportDER-pattern PubKeyExportPEM = PrivKeyExportPEM- -- NOTE: Different from allocBytesQuerying / INSUFFICIENT_BUFFER_SPACE pubKeyExport :: PubKey -- ^ __key__@@ -732,10 +705,10 @@ alloca $ \szPtr -> do poke szPtr 0 -- NOTE: Presumed be -1- _ <- botan_pubkey_export pkPtr nullPtr szPtr flags+ _ <- botan_pubkey_export pkPtr nullPtr szPtr (privKeyExportFlags flags) sz <- peek szPtr allocBytes (fromIntegral sz) $ \ bytesPtr -> do- throwBotanIfNegative_ $ botan_pubkey_export pkPtr bytesPtr szPtr flags+ throwBotanIfNegative_ $ botan_pubkey_export pkPtr bytesPtr szPtr (privKeyExportFlags flags) pubKeyAlgoName@@ -743,21 +716,22 @@ -> IO ByteString -- ^ __out[]__ pubKeyAlgoName = mkGetCString withPubKey botan_pubkey_algo_name -pubKeyCheckKey- :: PubKey -- ^ __key__- -> RNG -- ^ __rng__- -> CheckKeyFlags -- ^ __flags__- -> IO Bool-pubKeyCheckKey pk rng flags = withPubKey pk $ \ pkPtr -> do- withRNG rng $ \ botanRNG -> do- throwBotanCatchingSuccess $ botan_pubkey_check_key pkPtr botanRNG flags+pubKeyCheckKey ::+ PubKey -- ^ __key__+ -> RNG -- ^ __rng__+ -> CheckKeyFlags -- ^ __flags__+ -> IO Bool+pubKeyCheckKey pk rng flags =+ withPubKey pk $ \ pkPtr ->+ withRNG rng $ \ botanRNG ->+ throwBotanCatchingInvalidInput $ botan_pubkey_check_key pkPtr botanRNG (checkKeyFlags flags) -- Annoying - this mixes cint and csize -- I need to consolidate getsize / getint pubKeyEstimatedStrength :: PubKey -- ^ __key__ -> IO Int -- ^ __estimate__-pubKeyEstimatedStrength pk = fromIntegral <$> mkGetSize withPubKey botan_pubkey_estimated_strength pk+pubKeyEstimatedStrength pk = mkGetSize withPubKey botan_pubkey_estimated_strength pk pubKeyFingerprint :: PubKey -- ^ __key__@@ -802,47 +776,67 @@ -- Helpers mkPrivKeyLoad1_name- :: (Ptr BotanPrivKey -> BotanMP -> ConstPtr CChar -> IO BotanErrorCode)+ :: (Ptr BotanPrivKey -> BotanMP -> ConstPtr CChar -> IO CInt) -> MP -> ByteString -> IO PrivKey mkPrivKeyLoad1_name load a name = withMP a $ \ aPtr -> do asCString name $ \ namePtr -> do createPrivKey $ \ out -> load out aPtr (ConstPtr namePtr) mkPrivKeyLoad3- :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode)+ :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> IO PrivKey-mkPrivKeyLoad3 load a b c = withMany withMP [a,b,c] $ \ [aPtr,bPtr,cPtr] -> do- createPrivKey $ \ out -> load out aPtr bPtr cPtr+mkPrivKeyLoad3 load a b c = withMany withMP [a,b,c] $ \case+ [aPtr,bPtr,cPtr] -> do+ createPrivKey $ \ out -> load out aPtr bPtr cPtr+ _ -> error "mkPrivKeyLoad3: impossible" mkPrivKeyLoad4- :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode)+ :: (Ptr BotanPrivKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> MP -> IO PrivKey-mkPrivKeyLoad4 load a b c d = withMany withMP [a,b,c,d] $ \ [aPtr,bPtr,cPtr,dPtr] -> do- createPrivKey $ \ out -> load out aPtr bPtr cPtr dPtr+mkPrivKeyLoad4 load a b c d = withMany withMP [a,b,c,d] $ \case+ [aPtr,bPtr,cPtr,dPtr] -> do+ createPrivKey $ \ out -> load out aPtr bPtr cPtr dPtr+ _ -> error "mkPrivKeyLoad4: impossible" -- mkPubKeyLoad2- :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> IO BotanErrorCode)+ :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> IO PubKey-mkPubKeyLoad2 load a b = withMany withMP [a,b] $ \ [aPtr,bPtr] -> do- createPubKey $ \ out -> load out aPtr bPtr+mkPubKeyLoad2 load a b = withMany withMP [a,b] $ \case+ [aPtr,bPtr] -> do+ createPubKey $ \ out -> load out aPtr bPtr+ _ -> error "mkPubKeyLoad2: impossible" mkPubKeyLoad2_name- :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> ConstPtr CChar -> IO BotanErrorCode)+ :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> ConstPtr CChar -> IO CInt) -> MP -> MP -> ByteString -> IO PubKey-mkPubKeyLoad2_name load x y name = withMany withMP [x,y] $ \ [xPtr,yPtr] -> do- asCString name $ \ namePtr -> do+mkPubKeyLoad2_name load x y name = withMany withMP [x,y] $ \case+ [xPtr,yPtr] -> do+ asCString name $ \ namePtr -> do createPubKey $ \ out -> load out xPtr yPtr (ConstPtr namePtr)+ _ -> error "mkPubKeyLoad2_name: impossible" mkPubKeyLoad3- :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode)+ :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> IO PubKey-mkPubKeyLoad3 load a b c = withMany withMP [a,b,c] $ \ [aPtr,bPtr,cPtr] -> do- createPubKey $ \ out -> load out aPtr bPtr cPtr+mkPubKeyLoad3 load a b c = withMany withMP [a,b,c] $ \case+ [aPtr,bPtr,cPtr] -> do+ createPubKey $ \ out -> load out aPtr bPtr cPtr+ _ -> error "mkPubKeyLoad3: impossible" mkPubKeyLoad4- :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO BotanErrorCode)+ :: (Ptr BotanPubKey -> BotanMP -> BotanMP -> BotanMP -> BotanMP -> IO CInt) -> MP -> MP -> MP -> MP -> IO PubKey-mkPubKeyLoad4 load a b c d = withMany withMP [a,b,c,d] $ \ [aPtr,bPtr,cPtr,dPtr] -> do- createPubKey $ \ out -> load out aPtr bPtr cPtr dPtr+mkPubKeyLoad4 load a b c d = withMany withMP [a,b,c,d] $ \case+ [aPtr,bPtr,cPtr,dPtr] -> do+ createPubKey $ \ out -> load out aPtr bPtr cPtr dPtr+ _ -> error "mkPubKeyLoad4: impossible"++withMany ::+ (forall a . object -> (cobject -> IO a) -> IO a)+ -> [object]+ -> ([cobject] -> IO b)+ -> IO b+withMany _withObject [] act = act []+withMany withObject (obj:objs) act = withObject obj $ \ cobj -> withMany withObject objs (act . (cobj:))
src/Botan/Low/PubKey/DH.hs view
@@ -9,17 +9,14 @@ Portability : POSIX -} -module Botan.Low.PubKey.DH where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey.DH+module Botan.Low.PubKey.DH (+ privKeyLoadDH+ , pubKeyLoadDH+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey+import Botan.Bindings.PubKey.DH+import Botan.Low.MPI+import Botan.Low.PubKey privKeyLoadDH :: MP -- ^ __p__: prime order of a Z_p group
src/Botan/Low/PubKey/DSA.hs view
@@ -9,19 +9,16 @@ Portability : POSIX -} -module Botan.Low.PubKey.DSA where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.DSA+module Botan.Low.PubKey.DSA (+ privKeyCreateDSA+ , privKeyLoadDSA+ , pubKeyLoadDSA+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.RNG+import Botan.Bindings.PubKey.DSA+import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.RNG privKeyCreateDSA :: RNG -- ^ __rng__: initialized PRNG
src/Botan/Low/PubKey/Decrypt.hs view
@@ -9,29 +9,33 @@ Portability : POSIX -} -module Botan.Low.PubKey.Decrypt-(---- * Public key decryption- Decrypt(..)-, withDecrypt-, decryptCreate-, decryptDestroy-, decryptOutputLength-, decrypt--) where+module Botan.Low.PubKey.Decrypt ( -import qualified Data.ByteString as ByteString+ -- * Public key decryption+ Decrypt(..)+ , withDecrypt+ , decryptCreate+ , decryptDestroy+ , decryptOutputLength+ , decrypt -import Botan.Bindings.PubKey.Decrypt+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.Decrypt+import Botan.Low.Error.Internal (throwBotanIfNegative_)+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Internal as BSI+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- /* -- * Public Key Decryption@@ -39,14 +43,13 @@ newtype Decrypt = MkDecrypt { getDecryptForeignPtr :: ForeignPtr BotanPKOpDecryptStruct } -newDecrypt :: BotanPKOpDecrypt -> IO Decrypt withDecrypt :: Decrypt -> (BotanPKOpDecrypt -> IO a) -> IO a decryptDestroy :: Decrypt -> IO () createDecrypt :: (Ptr BotanPKOpDecrypt -> IO CInt) -> IO Decrypt-(newDecrypt, withDecrypt, decryptDestroy, createDecrypt, _)+(withDecrypt, decryptDestroy, createDecrypt) = mkBindings- MkBotanPKOpDecrypt runBotanPKOpDecrypt- MkDecrypt getDecryptForeignPtr+ MkBotanPKOpDecrypt (.runBotanPKOpDecrypt)+ MkDecrypt (.getDecryptForeignPtr) botan_pk_op_decrypt_destroy decryptCreate@@ -61,25 +64,28 @@ paddingPtr BOTAN_PUBKEY_DECRYPT_FLAGS_NONE --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withDecryptCreate :: PrivKey -> EMEName -> (Decrypt -> IO a) -> IO a-withDecryptCreate = mkWithTemp2 decryptCreate decryptDestroy- decryptOutputLength :: Decrypt -- ^ __op__ -> Int -- ^ __ctext_len__ -> IO Int -- ^ __ptext_len__ decryptOutputLength = mkGetSize_csize withDecrypt botan_pk_op_decrypt_output_length -decrypt- :: Decrypt -- ^ __op__- -> ByteString -- ^ __ciphertext__- -> IO ByteString -- ^ __plaintext__-decrypt dec ctext = withDecrypt dec $ \ decPtr -> do- asBytesLen ctext $ \ ctextPtr ctextLen -> do- allocBytesQuerying $ \ outPtr szPtr -> botan_pk_op_decrypt+decrypt ::+ Decrypt -- ^ __op__+ -> ByteString -- ^ __ciphertext__+ -> IO ByteString -- ^ __plaintext__+decrypt dec ctext =+ withDecrypt dec $ \ decPtr ->+ asBytesLen ctext $ \ ctextPtr ctextLen ->+ alloca $ \szPtr -> do+ sz <- decryptOutputLength dec (BS.length ctext)+ poke szPtr (fromIntegral sz)+ BSI.createAndTrim sz $ \outPtr -> do+ throwBotanIfNegative_ $+ botan_pk_op_decrypt decPtr outPtr szPtr (ConstPtr ctextPtr) ctextLen+ fromIntegral <$> peek szPtr
src/Botan/Low/PubKey/ECDH.hs view
@@ -1,11 +1,12 @@-module Botan.Low.PubKey.ECDH where--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.ECDH+module Botan.Low.PubKey.ECDH (+ privKeyLoadECDH+ , pubKeyLoadECDH+ ) where -import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey+import Botan.Bindings.PubKey.ECDH+import Botan.Low.MPI+import Botan.Low.PubKey+import Data.ByteString (ByteString) privKeyLoadECDH :: MP -- ^ __scalar__
src/Botan/Low/PubKey/ECDSA.hs view
@@ -1,11 +1,12 @@-module Botan.Low.PubKey.ECDSA where--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.ECDSA+module Botan.Low.PubKey.ECDSA (+ privKeyLoadECDSA+ , pubKeyLoadECDSA+ ) where -import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey+import Botan.Bindings.PubKey.ECDSA+import Botan.Low.MPI+import Botan.Low.PubKey+import Data.ByteString (ByteString) privKeyLoadECDSA :: MP -- ^ __scalar__
src/Botan/Low/PubKey/Ed25519.hs view
@@ -9,18 +9,19 @@ Portability : POSIX -} -module Botan.Low.PubKey.Ed25519 where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.Ed25519+module Botan.Low.PubKey.Ed25519 (+ privKeyLoadEd25519+ , pubKeyLoadEd25519+ , privKeyEd25519GetPrivKey+ , pubKeyEd25519GetPubKey+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.Remake+import Botan.Bindings.PubKey.Ed25519+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.PubKey+import Botan.Low.Remake+import Data.ByteString (ByteString) -- /* -- * Algorithm specific key operations: Ed25519
src/Botan/Low/PubKey/ElGamal.hs view
@@ -9,19 +9,16 @@ Portability : POSIX -} -module Botan.Low.PubKey.ElGamal where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.ElGamal+module Botan.Low.PubKey.ElGamal (+ privKeyCreateElGamal+ , privKeyLoadElGamal+ , pubKeyLoadElGamal+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.RNG+import Botan.Bindings.PubKey.ElGamal+import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.RNG -- /* -- * Algorithm specific key operations: ElGamal
src/Botan/Low/PubKey/Encrypt.hs view
@@ -9,29 +9,34 @@ Portability : POSIX -} -module Botan.Low.PubKey.Encrypt-(---- * Public key encryption- Encrypt(..)-, withEncrypt-, encryptCreate-, encryptDestroy-, encryptOutputLength-, encrypt--) where+module Botan.Low.PubKey.Encrypt ( -import qualified Data.ByteString as ByteString+ -- * Public key encryption+ Encrypt(..)+ , withEncrypt+ , encryptCreate+ , encryptDestroy+ , encryptOutputLength+ , encrypt -import Botan.Bindings.PubKey.Encrypt+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.Encrypt+import Botan.Low.Error.Internal (throwBotanIfNegative_)+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Botan.Low.RNG+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Internal as BSI+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- /* -- * Public Key Encryption@@ -39,14 +44,13 @@ newtype Encrypt = MkEncrypt { getEncryptForeignPtr :: ForeignPtr BotanPKOpEncryptStruct } -newEncrypt :: BotanPKOpEncrypt -> IO Encrypt withEncrypt :: Encrypt -> (BotanPKOpEncrypt -> IO a) -> IO a encryptDestroy :: Encrypt -> IO () createEncrypt :: (Ptr BotanPKOpEncrypt -> IO CInt) -> IO Encrypt-(newEncrypt, withEncrypt, encryptDestroy, createEncrypt, _)+(withEncrypt, encryptDestroy, createEncrypt) = mkBindings- MkBotanPKOpEncrypt runBotanPKOpEncrypt- MkEncrypt getEncryptForeignPtr+ MkBotanPKOpEncrypt (.runBotanPKOpEncrypt)+ MkEncrypt (.getEncryptForeignPtr) botan_pk_op_encrypt_destroy encryptCreate@@ -61,34 +65,31 @@ (ConstPtr paddingPtr) 0 --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withEncryptCreate :: PubKey -> EMEName -> (Encrypt -> IO a) -> IO a-withEncryptCreate = mkWithTemp2 encryptCreate encryptDestroy- encryptOutputLength :: Encrypt -- ^ __op__ -> Int -- ^ __ptext_len__ -> IO Int -- ^ __ctext_len__ encryptOutputLength = mkGetSize_csize withEncrypt botan_pk_op_encrypt_output_length --- NOTE: This properly takes advantage of szPtr, queries the buffer size - do this elsewhere--- NOTE: SM2 take a hash instead of a padding, and encrypt fails with InsufficientBufferSpace--- if sm2p256v1 is not used as the curve when creating the key (but creating the key and--- the encryption context do not fail)--- This implies that encryptOutputLength may be wrong or hardcoded for SM2 or that we--- are not supposed to use curves other than sm2p256v1 - this needs investigating-encrypt- :: Encrypt -- ^ __op__- -> RNG -- ^ __rng__- -> ByteString -- ^ __plaintext[]__- -> IO ByteString -- ^ __ciphertext[]__-encrypt enc rng ptext = withEncrypt enc $ \ encPtr -> do- withRNG rng $ \ botanRNG -> do- asBytesLen ptext $ \ ptextPtr ptextLen -> do- allocBytesQuerying $ \ outPtr szPtr -> botan_pk_op_encrypt- encPtr- botanRNG- outPtr- szPtr- ptextPtr- ptextLen+encrypt ::+ Encrypt -- ^ __op__+ -> RNG -- ^ __rng__+ -> ByteString -- ^ __plaintext[]__+ -> IO ByteString -- ^ __ciphertext[]__+encrypt enc rng ptext =+ withEncrypt enc $ \ encPtr ->+ withRNG rng $ \ botanRNG ->+ asBytesLen ptext $ \ ptextPtr ptextLen ->+ alloca $ \szPtr -> do+ sz <- encryptOutputLength enc (BS.length ptext)+ poke szPtr (fromIntegral sz)+ BSI.createAndTrim sz $ \outPtr -> do+ throwBotanIfNegative_ $+ botan_pk_op_encrypt+ encPtr+ botanRNG+ outPtr+ szPtr+ ptextPtr+ ptextLen+ fromIntegral <$> peek szPtr
src/Botan/Low/PubKey/KeyAgreement.hs view
@@ -9,35 +9,39 @@ Portability : POSIX -} -module Botan.Low.PubKey.KeyAgreement-(+module Botan.Low.PubKey.KeyAgreement ( --- * PK Key Agreement--- $introduction--- * Usage--- $usage+ -- * PK Key Agreement+ -- $introduction+ -- * Usage+ -- $usage --- * Key agreement- KeyAgreement(..)-, withKeyAgreement-, keyAgreementCreate-, keyAgreementDestroy-, keyAgreementExportPublic-, keyAgreementSize-, keyAgreement+ -- * Key agreement+ KeyAgreement(..)+ , withKeyAgreement+ , keyAgreementCreate+ , keyAgreementDestroy+ , keyAgreementExportPublic+ , keyAgreementSize+ , keyAgreement -) where+ ) where +import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.KeyAgreement+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.KDF+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey.KeyAgreement--import Botan.Low.Error-import Botan.Low.KDF-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.Remake+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable {- $introduction @@ -113,89 +117,70 @@ newtype KeyAgreement = MkKeyAgreement { getKeyAgreementForeignPtr :: ForeignPtr BotanPKOpKeyAgreementStruct } -newKeyAgreement :: BotanPKOpKeyAgreement -> IO KeyAgreement withKeyAgreement :: KeyAgreement -> (BotanPKOpKeyAgreement -> IO a) -> IO a keyAgreementDestroy :: KeyAgreement -> IO () createKeyAgreement :: (Ptr BotanPKOpKeyAgreement -> IO CInt) -> IO KeyAgreement-(newKeyAgreement, withKeyAgreement, keyAgreementDestroy, createKeyAgreement, _)+(withKeyAgreement, keyAgreementDestroy, createKeyAgreement) = mkBindings- MkBotanPKOpKeyAgreement runBotanPKOpKeyAgreement- MkKeyAgreement getKeyAgreementForeignPtr+ MkBotanPKOpKeyAgreement (.runBotanPKOpKeyAgreement)+ MkKeyAgreement (.getKeyAgreementForeignPtr) botan_pk_op_key_agreement_destroy -- NOTE: Silently uses the system RNG-keyAgreementCreate- :: PrivKey -- ^ __key__- -> KDFName -- ^ __kdf__- -> IO KeyAgreement -- ^ __op__-keyAgreementCreate sk algo = withPrivKey sk $ \ skPtr -> do- asCString algo $ \ algoPtr -> do- createKeyAgreement $ \ out -> botan_pk_op_key_agreement_create- out- skPtr- (ConstPtr algoPtr)- 0---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withKeyAgreementCreate :: PrivKey -> KDFName -> (KeyAgreement -> IO a) -> IO a-withKeyAgreementCreate = mkWithTemp2 keyAgreementCreate keyAgreementDestroy+keyAgreementCreate ::+ PrivKey -- ^ __key__+ -> KDFName -- ^ __kdf__+ -> IO KeyAgreement -- ^ __op__+keyAgreementCreate sk algo =+ withPrivKey sk $ \ skPtr ->+ asCString algo $ \ algoPtr ->+ createKeyAgreement $ \ out ->+ botan_pk_op_key_agreement_create+ out+ skPtr+ (ConstPtr algoPtr)+ 0 -- NOTE: I do not know if this provides a different functionality than just being -- an alias for botan_privkey_export_pubkey / privKeyExportPubKey -- Observe that it *does* just take a privkey, instead of a keyagreement -- It may simply be here for convenience.-{--int botan_pk_op_key_agreement_export_public(botan_privkey_t key, uint8_t out[], size_t* out_len) {- return copy_view_bin(out, out_len, botan_pk_op_key_agreement_view_public, key);-}--int botan_pk_op_key_agreement_view_public(botan_privkey_t key, botan_view_ctx ctx, botan_view_bin_fn view) {- return BOTAN_FFI_VISIT(key, [=](const auto& k) -> int {- if(auto kak = dynamic_cast<const Botan::PK_Key_Agreement_Key*>(&k))- return invoke_view_callback(view, ctx, kak->public_value());- else- return BOTAN_FFI_ERROR_INVALID_INPUT;- });-}--}-keyAgreementExportPublic- :: PrivKey -- ^ __key__- -> IO ByteString -- ^ __out[]__-keyAgreementExportPublic sk = withPrivKey sk $ \ skPtr -> do- allocBytesQuerying $ \ outPtr outLen -> botan_pk_op_key_agreement_export_public+keyAgreementExportPublic ::+ PrivKey -- ^ __key__+ -> IO ByteString -- ^ __out[]__+keyAgreementExportPublic sk =+ withPrivKey sk $ \ skPtr ->+ allocBytesQuerying $ \ outPtr outLen ->+ botan_pk_op_key_agreement_export_public skPtr outPtr outLen -keyAgreementSize- :: KeyAgreement -- ^ __op__- -> IO Int -- ^ __out_len__+keyAgreementSize ::+ KeyAgreement -- ^ __op__+ -> IO Int -- ^ __out_len__ keyAgreementSize = mkGetSize withKeyAgreement botan_pk_op_key_agreement_size --- | TODO: This function was leaking memory and causing crashes. Please observe--- carefully and report any future leaks. See issue #35.-keyAgreement- :: KeyAgreement -- ^ __op__- -> ByteString -- ^ __out[]__- -> ByteString -- ^ __other_key[]__- -> IO ByteString -- ^ __salt[]__-keyAgreement ka key salt = withKeyAgreement ka $ \ kaPtr -> do- asBytesLen key $ \ keyPtr keyLen -> do- asBytesLen salt $ \ saltPtr saltLen -> do- outSz <- keyAgreementSize ka- alloca $ \ szPtr -> do- -- NOTE: This poke was necessary to stop a memory leak- -- Similar pokes have been needed elsewere- -- TODO: Ensure that all alloca szPtr elsewhere are properly poked- poke szPtr (fromIntegral outSz)- out <- allocBytes outSz $ \ outPtr -> do- throwBotanIfNegative_ $ botan_pk_op_key_agreement- kaPtr- outPtr- szPtr- (ConstPtr keyPtr)- keyLen- (ConstPtr saltPtr)- saltLen- sz <- fromIntegral <$> peek szPtr- return $! ByteString.take sz out+keyAgreement ::+ KeyAgreement -- ^ __op__+ -> ByteString -- ^ __out[]__+ -> ByteString -- ^ __other_key[]__+ -> IO ByteString -- ^ __salt[]__+keyAgreement ka key salt =+ withKeyAgreement ka $ \ kaPtr ->+ asBytesLen key $ \ keyPtr keyLen ->+ asBytesLen salt $ \ saltPtr saltLen -> do+ outSz <- keyAgreementSize ka+ alloca $ \ szPtr -> do+ poke szPtr (fromIntegral outSz)+ out <- allocBytes outSz $ \ outPtr -> do+ throwBotanIfNegative_ $ botan_pk_op_key_agreement+ kaPtr+ outPtr+ szPtr+ (ConstPtr keyPtr)+ keyLen+ (ConstPtr saltPtr)+ saltLen+ sz <- fromIntegral <$> peek szPtr+ return $! ByteString.take sz out
src/Botan/Low/PubKey/KeyEncapsulation.hs view
@@ -9,46 +9,51 @@ Portability : POSIX -} -module Botan.Low.PubKey.KeyEncapsulation-(+module Botan.Low.PubKey.KeyEncapsulation ( --- * PK Key Encapsulation--- $introduction--- * Usage--- $usage+ -- * PK Key Encapsulation+ -- $introduction+ -- * Usage+ -- $usage --- * KEM Encryption- KEMSharedKey(..)-, KEMEncapsulatedKey(..)-, KEMEncrypt(..)-, withKEMEncrypt-, kemEncryptDestroy-, kemEncryptCreate-, kemEncryptSharedKeyLength-, kemEncryptEncapsulatedKeyLength-, kemEncryptCreateSharedKey+ -- * KEM Encryption+ KEMSharedKey+ , KEMEncapsulatedKey+ , KEMEncrypt(..)+ , withKEMEncrypt+ , kemEncryptDestroy+ , kemEncryptCreate+ , kemEncryptSharedKeyLength+ , kemEncryptEncapsulatedKeyLength+ , kemEncryptCreateSharedKey --- * KEM Decryption-, KEMDecrypt(..)-, withKEMDecrypt-, kemDecryptDestroy-, kemDecryptCreate-, kemDecryptSharedKeyLength-, kemDecryptSharedKey+ -- * KEM Decryption+ , KEMDecrypt(..)+ , withKEMDecrypt+ , kemDecryptDestroy+ , kemDecryptCreate+ , kemDecryptSharedKeyLength+ , kemDecryptSharedKey -) where+ ) where +import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.KeyEncapsulation+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.KDF+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Botan.Low.RNG+import Control.DeepSeq+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey.KeyEncapsulation--import Botan.Low.Error-import Botan.Low.KDF-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.Remake-import Botan.Low.RNG+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable {- $introduction @@ -117,14 +122,13 @@ newtype KEMEncrypt = MkKEMEncrypt { getKEMEncryptForeignPtr :: ForeignPtr BotanPKOpKEMEncryptStruct } -newKEMEncrypt :: BotanPKOpKEMEncrypt -> IO KEMEncrypt withKEMEncrypt :: KEMEncrypt -> (BotanPKOpKEMEncrypt -> IO a) -> IO a kemEncryptDestroy :: KEMEncrypt -> IO () createKEMEncrypt :: (Ptr BotanPKOpKEMEncrypt -> IO CInt) -> IO KEMEncrypt-(newKEMEncrypt, withKEMEncrypt, kemEncryptDestroy, createKEMEncrypt, _)+(withKEMEncrypt, kemEncryptDestroy, createKEMEncrypt) = mkBindings- MkBotanPKOpKEMEncrypt runBotanPKOpKEMEncrypt- MkKEMEncrypt getKEMEncryptForeignPtr+ MkBotanPKOpKEMEncrypt (.runBotanPKOpKEMEncrypt)+ MkKEMEncrypt (.getKEMEncryptForeignPtr) botan_pk_op_kem_encrypt_destroy @@ -142,10 +146,6 @@ pkPtr (ConstPtr algoPtr) --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withKEMEncryptCreate :: PubKey -> KDFName -> (KEMEncrypt -> IO a) -> IO a-withKEMEncryptCreate = mkWithTemp2 kemEncryptCreate kemEncryptDestroy- kemEncryptSharedKeyLength :: KEMEncrypt -- ^ __op__ -> Int -- ^ __desired_shared_key_length__@@ -193,14 +193,13 @@ newtype KEMDecrypt = MkKEMDecrypt { getKEMDecryptForeignPtr :: ForeignPtr BotanPKOpKEMDecryptStruct } -newKEMDecrypt :: BotanPKOpKEMDecrypt -> IO KEMDecrypt withKEMDecrypt :: KEMDecrypt -> (BotanPKOpKEMDecrypt -> IO a) -> IO a kemDecryptDestroy :: KEMDecrypt -> IO () createKEMDecrypt :: (Ptr BotanPKOpKEMDecrypt -> IO CInt) -> IO KEMDecrypt-(newKEMDecrypt, withKEMDecrypt, kemDecryptDestroy, createKEMDecrypt, _)+(withKEMDecrypt, kemDecryptDestroy, createKEMDecrypt) = mkBindings- MkBotanPKOpKEMDecrypt runBotanPKOpKEMDecrypt- MkKEMDecrypt getKEMDecryptForeignPtr+ MkBotanPKOpKEMDecrypt (.runBotanPKOpKEMDecrypt)+ MkKEMDecrypt (.getKEMDecryptForeignPtr) botan_pk_op_kem_decrypt_destroy kemDecryptCreate@@ -213,10 +212,6 @@ out skPtr (ConstPtr algoPtr)---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withKEMDecryptCreate :: PrivKey -> KDFName -> (KEMDecrypt -> IO a) -> IO a-withKEMDecryptCreate = mkWithTemp2 kemDecryptCreate kemDecryptDestroy kemDecryptSharedKeyLength :: KEMDecrypt -- ^ __op__
src/Botan/Low/PubKey/RSA.hs view
@@ -9,17 +9,19 @@ Portability : POSIX -} -module Botan.Low.PubKey.RSA where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey.RSA+module Botan.Low.PubKey.RSA (+ privKeyLoadRSA+ , privKeyLoadRSA_PKCS1+ , privKeyRSAGetPrivKey+ , pubKeyLoadRSA+ ) where -import Botan.Low.Error-import Botan.Low.Remake-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey+import Botan.Bindings.PubKey.RSA+import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Data.Word -- /* -- * Algorithm specific key operations: RSA
src/Botan/Low/PubKey/SM2.hs view
@@ -1,11 +1,12 @@-module Botan.Low.PubKey.SM2 where--import Botan.Bindings.PubKey-import Botan.Bindings.PubKey.SM2+module Botan.Low.PubKey.SM2 (+ privKeyLoadSM2+ , pubKeyLoadSM2+ ) where -import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey+import Botan.Bindings.PubKey.SM2+import Botan.Low.MPI+import Botan.Low.PubKey+import Data.ByteString (ByteString) privKeyLoadSM2 :: MP -- ^ __scalar__
src/Botan/Low/PubKey/Sign.hs view
@@ -9,33 +9,39 @@ Portability : POSIX -} -module Botan.Low.PubKey.Sign-(+module Botan.Low.PubKey.Sign ( --- * Public key signatures- Sign(..)-, SigningFlags(..)-, pattern StandardFormatSignature-, pattern DERFormatSignature-, withSign-, signCreate-, signDestroy-, signOutputLength-, signUpdate-, signFinish+ -- * Public key signatures+ Sign(..)+ , SigningFlags+ , pattern StandardFormatSignature+ , pattern DERFormatSignature+ , withSign+ , signCreate+ , signDestroy+ , signOutputLength+ , signUpdate+ , signFinish -) where+ ) where +import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.Sign+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Botan.Low.RNG+import Control.DeepSeq+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey.Sign--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.Remake+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- /* -- * Signature Generation@@ -43,19 +49,15 @@ newtype Sign = MkSign { getSignForeignPtr :: ForeignPtr BotanPKOpSignStruct } -newSign :: BotanPKOpSign -> IO Sign withSign :: Sign -> (BotanPKOpSign -> IO a) -> IO a signDestroy :: Sign -> IO () createSign :: (Ptr BotanPKOpSign -> IO CInt) -> IO Sign-(newSign, withSign, signDestroy, createSign, _)+(withSign, signDestroy, createSign) = mkBindings- MkBotanPKOpSign runBotanPKOpSign- MkSign getSignForeignPtr+ MkBotanPKOpSign (.runBotanPKOpSign)+ MkSign (.getSignForeignPtr) botan_pk_op_sign_destroy --- TODO: Rename SignAlgoParams / SigningParams-type SignAlgoName = ByteString- type SigningFlags = Word32 pattern StandardFormatSignature -- ^ Not an actual flags@@ -76,10 +78,6 @@ skPtr (ConstPtr algoPtr) flags---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withSignCreate :: PrivKey -> EMSAName -> SigningFlags -> (Sign -> IO a) -> IO a-withSignCreate = mkWithTemp3 signCreate signDestroy signOutputLength :: Sign -- ^ __op__
src/Botan/Low/PubKey/Verify.hs view
@@ -9,30 +9,29 @@ Portability : POSIX -} -module Botan.Low.PubKey.Verify-(---- * Public key signature verification- Verify(..)-, withVerify-, verifyCreate-, verifyDestroy-, verifyUpdate-, verifyFinish--) where+module Botan.Low.PubKey.Verify ( -import qualified Data.ByteString as ByteString+ -- * Public key signature verification+ Verify(..)+ , withVerify+ , verifyCreate+ , verifyDestroy+ , verifyUpdate+ , verifyFinish -import Botan.Bindings.PubKey.Verify+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.Sign (SigningFlags(..))-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PubKey.Verify+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.PubKey+import Botan.Low.PubKey.Sign (SigningFlags)+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr -- /* -- * Signature Verification@@ -40,18 +39,15 @@ newtype Verify = MkVerify { getVerifyForeignPtr :: ForeignPtr BotanPKOpVerifyStruct } -newVerify :: BotanPKOpVerify -> IO Verify withVerify :: Verify -> (BotanPKOpVerify -> IO a) -> IO a verifyDestroy :: Verify -> IO () createVerify :: (Ptr BotanPKOpVerify -> IO CInt) -> IO Verify-(newVerify, withVerify, verifyDestroy, createVerify, _)+(withVerify, verifyDestroy, createVerify) = mkBindings- MkBotanPKOpVerify runBotanPKOpVerify- MkVerify getVerifyForeignPtr+ MkBotanPKOpVerify (.runBotanPKOpVerify)+ MkVerify (.getVerifyForeignPtr) botan_pk_op_verify_destroy -type VerifyAlgo = ByteString- verifyCreate :: PubKey -- ^ __key__ -> EMSAName -- ^ __hash_and_padding__@@ -65,10 +61,6 @@ (ConstPtr algoPtr) flags --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withVerifyCreate :: PubKey -> EMSAName -> SigningFlags -> (Verify -> IO a) -> IO a-withVerifyCreate = mkWithTemp3 verifyCreate verifyDestroy- verifyUpdate :: Verify -- ^ __op__ -> ByteString -- ^ __in[]__@@ -76,13 +68,14 @@ verifyUpdate = mkWithObjectSetterCBytesLen withVerify botan_pk_op_verify_update -- TODO: Signature type-verifyFinish- :: Verify -- ^ __op__- -> ByteString -- ^ __sig[]__- -> IO Bool-verifyFinish verify sig = withVerify verify $ \ verifyPtr -> do- asBytesLen sig $ \ sigPtr sigLen -> do- throwBotanCatchingSuccess $ botan_pk_op_verify_finish- verifyPtr- (ConstPtr sigPtr)- sigLen+verifyFinish ::+ Verify -- ^ __op__+ -> ByteString -- ^ __sig[]__+ -> IO Bool+verifyFinish verify sig =+ withVerify verify $ \ verifyPtr ->+ asBytesLen sig $ \ sigPtr sigLen ->+ throwBotanCatchingInvalidVerifier $ botan_pk_op_verify_finish+ verifyPtr+ (ConstPtr sigPtr)+ sigLen
src/Botan/Low/PubKey/X25519.hs view
@@ -9,20 +9,19 @@ Portability : POSIX -} -module Botan.Low.PubKey.X25519 where--import qualified Data.ByteString as ByteString--import Botan.Bindings.PubKey--import Botan.Bindings.PubKey.X25519+module Botan.Low.PubKey.X25519 (+ privKeyLoadX25519+ , pubKeyLoadX25519+ , privKeyX25519GetPrivKey+ , pubKeyX25519GetPubKey+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.Remake+import Botan.Bindings.PubKey.X25519+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.PubKey+import Botan.Low.Remake+import Data.ByteString (ByteString) -- /* -- * Algorithm specific key operations: X25519
src/Botan/Low/PwdHash.hs view
@@ -8,129 +8,224 @@ Stability : experimental Portability : POSIX -Derive a key from a passphrase+This module is based on the [Password Based Key+Deriviation](https://botan.randombit.net/handbook/api_ref/pbkdf.html) section of+the C++ API reference. -} -module Botan.Low.PwdHash-(+module Botan.Low.PwdHash (+ pwdhash+ , pwdhashTimed+ -- * Available schemes+ -- $available-schemes+ , PBKDFName+ , pattern PBKDF2+ , pbkdf2+ , pattern Scrypt+ , pattern Argon2d+ , pattern Argon2i+ , pattern Argon2id+ , pattern Bcrypt_PBKDF+ , pattern OpenPGP_S2K+ , openPGP_S2K+ ) where --- * Password hashing+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.PwdHash+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Internal.String+import Data.ByteString (ByteString)+import Foreign.Marshal.Alloc+import Foreign.Storable - PBKDFName(..)-, pwdhash-, pwdhashTimed+{- $setup+>>> import Botan.Low.Hash+-} --- * Password hashing algorithms+{-------------------------------------------------------------------------------+ Password hashing+-------------------------------------------------------------------------------} -, pattern PBKDF2-, pbkdf2-, pattern Scrypt-, pattern Argon2d-, pattern Argon2i-, pattern Argon2id-, pattern Bcrypt_PBKDF-, pattern OpenPGP_S2K-, openPGP_S2K+-- | Derive a cryptographic key from a passphrase using algorithm-specific parameters+--+-- NOTE: the interpretation of parameters @param1@, @param2@, and @param3@ are+-- different depending on the PBKDF algorithm that is picked. See the+-- documentation of the+-- [@from_params@](https://botan.randombit.net/handbook/api_ref/pbkdf.html#passwordhash)+-- C++ function for more information about the meaning of the parameters.+--+pwdhash ::+ PBKDFName -- ^ __algo__: PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"+ -> Int -- ^ __param1__: the first PBKDF algorithm parameter+ -> Int -- ^ __param2__: the second PBKDF algorithm parameter (may be zero if unneeded)+ -> Int -- ^ __param3__: the third PBKDF algorithm parameter (may be zero if unneeded)+ -> Int -- ^ __out_len__: the desired length of the key to produce+ -> ByteString -- ^ __passphrase__: the password to derive the key from+ -> ByteString -- ^ __salt[]__: a randomly chosen salt+ -> IO ByteString -- ^ __out[]__: buffer to store the derived key, must be of out_len bytes+pwdhash algo p1 p2 p3 outLen passphrase salt =+ allocBytes outLen $ \ outPtr ->+ asCString algo $ \ algoPtr ->+ asCStringLen passphrase $ \ passphrasePtr passphraseLen ->+ asBytesLen salt $ \ saltPtr saltLen ->+ throwBotanIfNegative_ $ botan_pwdhash+ (ConstPtr algoPtr)+ (fromIntegral p1)+ (fromIntegral p2)+ (fromIntegral p3)+ outPtr+ (fromIntegral outLen)+ (ConstPtr passphrasePtr)+ passphraseLen+ (ConstPtr saltPtr)+ saltLen -) where+-- | Derive a cryptographic key from a passphrase using algorithm-specific+-- parameters that are tuned automatically for a desired running time of the+-- algorithm.+--+-- NOTE: for the @Argon2@ and @Scrypt@ PBKDF algorithms, 'pwdhashTimed' returns+-- parameters in a different order than the order in which they should be passed+-- to 'pwdhash'. This is a known issue with the Botan C++ library. See+-- <https://github.com/randombit/botan/issues/2144> for more information.+--+-- 'pwdhashTimed' always returns parameters in this order:+--+-- > (iterations, parallelism, memoryParam)+--+-- 'pwdhash' shoulds be given parameters in this order for the+-- @Argon2@ and @Scrypt@ algorithms:+--+-- > param1 = memoryParam+-- > param2 = iterations+-- > param3 = parallelism+--+pwdhashTimed ::+ -- | __algo__: PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"+ PBKDFName+ -- | __msec__: the desired runtime in milliseconds+ -> Int+ -- | __out_len__: the desired length of the key to produce+ -> Int+ -- | __passphrase__: the password to derive the key from+ -> ByteString+ -- | __salt[]__: a randomly chosen salt+ -> ByteString+ -- | A tuple of four elements (in order):+ --+ -- * __param1__: the first PBKDF algorithm parameter+ -- * __param2__: the second PBKDF algorithm parameter (may be zero if unneeded)+ -- * __param3__: the third PBKDF algorithm parameter (may be zero if unneeded)+ -- * __out[]__: buffer to store the derived key, must be of out_len bytes+ -> IO (Int, Int, Int, ByteString)+pwdhashTimed algo msec outLen passphrase salt =+ alloca $ \ p1Ptr ->+ alloca $ \ p2Ptr ->+ alloca $ \ p3Ptr -> do+ out <- allocBytes outLen $ \ outPtr ->+ asCString algo $ \ algoPtr ->+ asCStringLen passphrase $ \ passphrasePtr passphraseLen ->+ asBytesLen salt $ \ saltPtr saltLen -> do+ throwBotanIfNegative_ $ botan_pwdhash_timed+ (ConstPtr algoPtr)+ (fromIntegral msec)+ p1Ptr+ p2Ptr+ p3Ptr+ outPtr+ (fromIntegral outLen)+ (ConstPtr passphrasePtr)+ passphraseLen+ (ConstPtr saltPtr)+ saltLen+ p1 <- fromIntegral <$> peek p1Ptr+ p2 <- fromIntegral <$> peek p2Ptr+ p3 <- fromIntegral <$> peek p3Ptr+ return (p1, p2, p3, out) -import qualified Data.ByteString as ByteString+{-------------------------------------------------------------------------------+ Available schemes+-------------------------------------------------------------------------------} -import Botan.Bindings.PwdHash+{- $available-schemes -import Botan.Low.Hash-import Botan.Low.MAC-import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude+A number of algorithms are available to be used as password derivation schemes.+An algorithm is selected by passing the /algorithm specification name/ of the+algorithm as a string to the password hashing functions. In some cases an+algorithm specification name is just the /algorithm name/, like @"Scrypt"@. In+other cases an algorithm specification name is a combination of an algorithm+name paired with some parameters, such as @"PKBDF2(SHA-256)"@ where @"PBKDF2"@+is the algorithm name followed by a single parameter. Pattern synonyms are+included for each available /algorithm name/. Where necessary, such as in the+@"PBKDF2"@ case, these algorithm names can be be manually extended to full+algorithm specification names, or by using utility functions like 'pbkdf2' that+this module also provides. +See the [Available+Schemes](https://botan.randombit.net/handbook/api_ref/pbkdf.html#available-schemes)+section of the C++ API reference for more information about available schemes.+-}++-- | The name of a key derivation scheme used as a PBKDF algorithm type PBKDFName = ByteString pattern PBKDF2- , Scrypt- , Argon2d- , Argon2i- , Argon2id- , Bcrypt_PBKDF- , OpenPGP_S2K- :: PBKDFName+ , Scrypt+ , Argon2d+ , Argon2i+ , Argon2id+ , Bcrypt_PBKDF+ , OpenPGP_S2K+ :: PBKDFName +-- |+-- >>> PBKDF2+-- "PBKDF2" pattern PBKDF2 = BOTAN_PBKDF_PBKDF2++-- |+-- >>> pbkdf2 SHA256+-- "PBKDF2(SHA-256)"+--+-- NOTE: "PBKDF(HMAC(SHA-256))" is equivalent to "PBKDF(SHA-256)". 'pbkdf2'+-- always outputs the latter given a hash function name.+pbkdf2 :: HashName -> PBKDFName+pbkdf2 m = PBKDF2 /$ m++-- |+-- >>> Scrypt+-- "Scrypt" pattern Scrypt = BOTAN_PBKDF_SCRYPT++-- |+-- >>> Argon2d+-- "Argon2d" pattern Argon2d = BOTAN_PBKDF_ARGON2D++-- |+-- >>> Argon2i+-- "Argon2i" pattern Argon2i = BOTAN_PBKDF_ARGON2I++-- |+-- >>> Argon2id+-- "Argon2id" pattern Argon2id = BOTAN_PBKDF_ARGON2ID++-- |+-- >>> Bcrypt_PBKDF+-- "Bcrypt-PBKDF" pattern Bcrypt_PBKDF = BOTAN_PBKDF_BCRYPT_PBKDF-pattern OpenPGP_S2K = BOTAN_PBKDF_OPENPGP_S2K --- NOTE: May require HMAC-pbkdf2 :: MACName -> PBKDFName-pbkdf2 m = PBKDF2 /$ m+-- |+-- >>> OpenPGP_S2K+-- "OpenPGP-S2K"+pattern OpenPGP_S2K = BOTAN_PBKDF_OPENPGP_S2K +-- |+-- >>> openPGP_S2K SHA256+-- "OpenPGP-S2K(SHA-256)" openPGP_S2K:: HashName -> PBKDFName openPGP_S2K h = OpenPGP_S2K /$ h---- NOTE: Should passphrase be Text or ByteString? Text is implied by use of const char*--- as well as the non-null context implied by passphrase_len == 0. ByteString for now.---- | Password hash------ 'pwdhash' and 'pwdhashTimed'\'s parameter order may be inconsistent. See--- botan-low\/test\/Botan\/Low\/PwdHashSpec.hs for more information.-pwdhash- :: PBKDFName -- ^ __algo__: PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"- -> Int -- ^ __param1__: the first PBKDF algorithm parameter- -> Int -- ^ __param2__: the second PBKDF algorithm parameter (may be zero if unneeded)- -> Int -- ^ __param3__: the third PBKDF algorithm parameter (may be zero if unneeded)- -> Int -- ^ __out_len__: the desired length of the key to produce- -> ByteString -- ^ __passphrase__: the password to derive the key from- -> ByteString -- ^ __salt[]__: a randomly chosen salt- -> IO ByteString -- ^ __out[]__: buffer to store the derived key, must be of out_len bytes-pwdhash algo p1 p2 p3 outLen passphrase salt = allocBytes outLen $ \ outPtr -> do- asCString algo $ \ algoPtr -> do- asCStringLen passphrase $ \ passphrasePtr passphraseLen -> do- asBytesLen salt $ \ saltPtr saltLen -> do- throwBotanIfNegative_ $ botan_pwdhash- (ConstPtr algoPtr)- (fromIntegral p1)- (fromIntegral p2)- (fromIntegral p3)- outPtr- (fromIntegral outLen)- (ConstPtr passphrasePtr)- passphraseLen- (ConstPtr saltPtr)- saltLen---- | Timed password hash------ 'pwdhash' and 'pwdhashTimed'\'s parameter order may be inconsistent. See--- botan-low\/test\/Botan\/Low\/PwdHashSpec.hs for more information.-pwdhashTimed- :: PBKDFName -- ^ __algo__: PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"- -> Int -- ^ __msec__: the desired runtime in milliseconds- -> Int -- ^ __out_len__: the desired length of the key to produce- -> ByteString -- ^ __passphrase__: the password to derive the key from- -> ByteString -- ^ __salt[]__: a randomly chosen salt- -> IO (Int,Int,Int,ByteString) -- ^ __out[]__: buffer to store the derived key, must be of out_len bytes-pwdhashTimed algo msec outLen passphrase salt = alloca $ \ p1Ptr -> alloca $ \ p2Ptr -> alloca $ \ p3Ptr -> do- out <- allocBytes outLen $ \ outPtr -> do- asCString algo $ \ algoPtr -> do- asCStringLen passphrase $ \ passphrasePtr passphraseLen -> do- asBytesLen salt $ \ saltPtr saltLen -> do- throwBotanIfNegative_ $ botan_pwdhash_timed- (ConstPtr algoPtr)- (fromIntegral msec)- p1Ptr- p2Ptr- p3Ptr- outPtr- (fromIntegral outLen)- (ConstPtr passphrasePtr)- passphraseLen- (ConstPtr saltPtr)- saltLen- p1 <- fromIntegral <$> peek p1Ptr- p2 <- fromIntegral <$> peek p2Ptr- p3 <- fromIntegral <$> peek p3Ptr- return (p1,p2,p3,out)
src/Botan/Low/RNG.hs view
@@ -11,43 +11,43 @@ Pseudo-random number generation. -} -module Botan.Low.RNG-(---- * Random number generators--- $introduction---- * Usage--- $usage+module Botan.Low.RNG ( - RNG(..)-, RNGType(..)-, withRNG-, rngInit-, rngDestroy-, rngGet-, systemRNGGet-, rngReseed-, rngReseedFromRNG-, rngAddEntropy+ -- * Random number generators+ -- $introduction --- * RNG Types+ -- * Usage+ -- $usage -, pattern SystemRNG-, pattern UserRNG-, pattern UserThreadsafeRNG-, pattern RDRandRNG+ RNG(..)+ , RNGType+ , withRNG+ , rngInit+ , rngDestroy+ , rngGet+ , systemRNGGet+ , rngReseed+ , rngReseedFromRNG+ , rngAddEntropy -) where+ -- * RNG Types -import qualified Data.ByteString as ByteString+ , pattern SystemRNG+ , pattern UserRNG+ , pattern UserThreadsafeRNG+ , pattern RDRandRNG -import Botan.Bindings.RNG+ ) where -import Botan.Low.Error ( throwBotanIfNegative_ )-import Botan.Low.Make ( mkWithTemp1 )-import Botan.Low.Remake-import Botan.Low.Prelude+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.RNG+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Remake+import Data.ByteString (ByteString)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Ptr {- $introduction @@ -92,13 +92,12 @@ newtype RNG = MkRNG { getRNGForeignPtr :: ForeignPtr BotanRNGStruct } -newRNG :: BotanRNG -> IO RNG withRNG :: RNG -> (BotanRNG -> IO a) -> IO a -- | Destroy a random number generator object immediately rngDestroy :: RNG -> IO () createRNG :: (Ptr BotanRNG -> IO CInt) -> IO RNG-(newRNG, withRNG, rngDestroy, createRNG, _)- = mkBindings MkBotanRNG runBotanRNG MkRNG getRNGForeignPtr botan_rng_destroy+(withRNG, rngDestroy, createRNG)+ = mkBindings MkBotanRNG (.runBotanRNG) MkRNG (.getRNGForeignPtr) botan_rng_destroy type RNGType = ByteString @@ -128,10 +127,6 @@ :: RNGType -- ^ __rng_type__: type of the rng -> IO RNG -- ^ __rng__ rngInit = mkCreateObjectCString createRNG botan_rng_init---- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withRNGInit :: RNGType -> (RNG -> IO a) -> IO a-withRNGInit = mkWithTemp1 rngInit rngDestroy -- | Get random bytes from a random number generator rngGet
src/Botan/Low/Remake.hs view
@@ -11,82 +11,33 @@ Generate low-level bindings automatically -} -module Botan.Low.Remake-( mkBindings-, mkCreateObject-, mkCreateObjects-, mkCreateObjectWith-, mkCreateObjectCString-, mkCreateObjectCString1-, mkCreateObjectCBytes-, mkCreateObjectCBytesLen-, mkCreateObjectCBytesLen1-, mkWithObjectAction-, mkWithObjectGetterCBytesLen-, mkWithObjectGetterCBytesLen1-, mkWithObjectSetterCString-, mkWithObjectSetterCBytesLen-) where--import Botan.Low.Prelude-import Botan.Low.Error--import qualified Data.ByteString.Internal as ByteString--import Botan.Low.Make (allocBytesQuerying)---- ByteString Helpers---- NOTE: Was allocBytes-createByteString :: Int -> (Ptr byte -> IO ()) -> IO ByteString-createByteString sz f = ByteString.create sz (f . castPtr)---- NOTE: Was allocBytesWith--- createByteString' :: Int -> (Ptr byte -> IO a) -> IO (a, ByteString)---- NOTE: Was allocBytesQuerying--- createByteStringQuerying :: (Ptr byte -> Ptr CSize -> IO CInt) -> IO ByteString--- createByteStringQuerying fn = do--- alloca $ \ szPtr -> do--- -- TODO: Maybe poke szPtr 0 for extra safety in cas its not initially zero--- code <- fn nullPtr szPtr--- case code of--- InsufficientBufferSpace -> do--- sz <- fromIntegral <$> peek szPtr--- allocBytes sz $ \ outPtr -> throwBotanIfNegative_ $ fn outPtr szPtr--- _ -> do--- throwBotanError code---- NOTE: Was allocBytesQueryingCString--- NOTE: Does not check length of taken string, vulnerable to null byte injection--- createByteStringQueryingCString :: (Ptr byte -> Ptr CSize -> IO CInt) -> IO ByteString--- createByteStringQueryingCString action = do--- cstring <- allocBytesQuerying action--- return $!! ByteString.takeWhile (/= 0) cstring-------- type NewObject object botan = botan -> IO object--- type WithObject object botan = (forall a . object -> (botan -> IO a) -> IO a)--- type DestroyObject object botan = object -> IO ()--- type CreateObject object botan = (Ptr botan -> IO CInt) -> IO object--- type CreateObjects object botan = (Ptr botan -> Ptr CSize -> IO CInt) -> IO object---- Example usage-{--newtype RNG = MkRNG { getRNGForeignPtr :: ForeignPtr BotanRNGStruct }+module Botan.Low.Remake (+ mkBindings+ , mkCreateObject+ , mkCreateObjectWith+ , mkCreateObjectCString+ , mkCreateObjectCString1+ , mkCreateObjectCBytes+ , mkCreateObjectCBytesLen+ , mkWithObjectAction+ , mkWithObjectGetterCBytesLen1+ , mkWithObjectSetterCBytesLen+ ) where -newRNG :: BotanRNG -> IO RNG-withRNG :: RNG -> (BotanRNG -> IO a) -> IO a-rngDestroy :: RNG -> IO ()-createRNG :: (Ptr BotanRNG -> IO CInt) -> IO RNG-(newRNG, withRNG, rngDestroy, createRNG, _)- = mkBindings MkBotanRNG runBotanRNG MkRNG getRNGForeignPtr botan_rng_destroy+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Control.Exception+import Data.ByteString (ByteString)+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable+import Prelude hiding (init) -rngInit :: RNGType -> IO RNG-rngInit name = asCString name $ \ namePtr -> do- createRNG $ \ outPtr -> botan_rng_init outPtr (ConstPtr namePtr)--} mkBindings :: (Storable botan) => (Ptr struct -> botan) -- mkBotan@@ -94,14 +45,12 @@ -> (ForeignPtr struct -> object) -- mkForeign -> (object -> ForeignPtr struct) -- runForeign -> FinalizerPtr struct -- destroy / finalizer- -> ( botan -> IO object -- newObject- , object -> (botan -> IO a) -> IO a -- withObject+ -> ( object -> (botan -> IO a) -> IO a -- withObject , object -> IO () -- destroyObject , (Ptr botan -> IO CInt) -> IO object -- createObject- , (Ptr botan -> Ptr CSize -> IO CInt) -> IO [object] -- createObjects ) mkBindings mkBotan runBotan mkForeign runForeign destroy = bindings where- bindings = (newObject, withObject, objectDestroy, createObject, createObjects)+ bindings = (withObject, objectDestroy, createObject) newObject botan = do foreignPtr <- newForeignPtr destroy (runBotan botan) return $ mkForeign foreignPtr@@ -112,7 +61,6 @@ -- objectFinalize obj = new stable foreign ptr ... destroy -- objectDestroy obj = withObject obj destroy createObject = mkCreateObject newObject- createObjects = mkCreateObjects newObject {- Create functions@@ -129,23 +77,6 @@ out <- peek outPtr newObject out --- TODO: Rename mkCreates-mkCreateObjects- :: (Storable botan)- => (botan -> IO object)- -> (Ptr botan -> Ptr CSize -> IO CInt)- -> IO [object]-mkCreateObjects newObject inits = mask_ $ alloca $ \ szPtr -> do- code <- inits nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ inits arrPtr szPtr- outs <- peekArray sz arrPtr- forM outs newObject- _ -> throwBotanError code- mkCreateObjectWith :: ((Ptr botan -> IO CInt) -> IO object) -> (arg -> (carg -> IO object) -> IO object)@@ -164,6 +95,8 @@ -- mkCreateObjectCString createObject init cstr = withCString cstr $ \ namePtr -> do -- createObject $ \ outPtr -> init outPtr (ConstPtr namePtr) mkCreateObjectCString createObject = mkCreateObjectWith createObject withConstCString+ where+ withConstCString bs k = withCString bs $ \ptr -> k (ConstPtr ptr) -- TODO: Rename mkCreateCString1 mkCreateObjectCString1@@ -195,15 +128,6 @@ mkCreateObjectCBytesLen createObject init bytes = withCBytesLen bytes $ \ (cbytes,len) -> do createObject $ \ out -> init out (ConstPtr cbytes) (fromIntegral len) -mkCreateObjectCBytesLen1- :: ((Ptr botan -> IO CInt) -> IO object)- -> (Ptr botan -> ConstPtr Word8 -> CSize -> a -> IO CInt)- -> ByteString- -> a- -> IO object-mkCreateObjectCBytesLen1 createObject init bytes a = withCBytesLen bytes $ \ (cbytes,len) -> do- createObject $ \ out -> init out (ConstPtr cbytes) (fromIntegral len) a- {- Action -}@@ -222,20 +146,8 @@ -} -- TODO: getter parameter order may be improper - switch up if problematic-mkWithObjectGetterCBytesLen- :: (forall a . object -> (botan -> IO a) -> IO a)- -> (botan -> Ptr Word8 -> Ptr CSize -> IO CInt)- -> object- -> IO ByteString-mkWithObjectGetterCBytesLen withObject getter obj = withObject obj $ \ cobj -> do- allocBytesQuerying $ \ outPtr outLen -> getter- cobj- outPtr- outLen---- TODO: getter parameter order may be improper - switch up if problematic mkWithObjectGetterCBytesLen1- :: (forall a . object -> (botan -> IO a) -> IO a)+ :: (forall b . object -> (botan -> IO b) -> IO b) -> (botan -> a -> Ptr Word8 -> Ptr CSize -> IO CInt) -> object -> a@@ -251,22 +163,11 @@ Setters -} --- TODO: Rename mkSetterCString-mkWithObjectSetterCString- :: (forall a . object -> (botan -> IO a) -> IO a)- -> (botan -> ConstPtr CChar -> IO BotanErrorCode)- -> object- -> ByteString- -> IO ()-mkWithObjectSetterCString withObject setter obj str = withObject obj $ \ cobj -> do- withCString str $ \ cstr -> do- throwBotanIfNegative_ $ setter cobj (ConstPtr cstr)- -- Replaces mkSetBytesLen -- TODO: Rename mkSetterCBytesLen mkWithObjectSetterCBytesLen :: (forall a . object -> (botan -> IO a) -> IO a)- -> (botan -> ConstPtr Word8 -> CSize -> IO BotanErrorCode)+ -> (botan -> ConstPtr Word8 -> CSize -> IO CInt) -> object -> ByteString -> IO ()
src/Botan/Low/SRP6.hs view
@@ -8,170 +8,263 @@ Stability : experimental Portability : POSIX -The library contains an implementation of the SRP6-a password-authenticated key exchange protocol.-+This module is based on the [Secure Remote+Password](https://botan.randombit.net/handbook/api_ref/srp.html) section of the+C++ API reference. -} -module Botan.Low.SRP6-(---- * Secure Random Password 6a--- $introduction---- * Usage--- $usage-- SRP6ServerSession(..)-, withSRP6ServerSession-, srp6ServerSessionInit-, srp6ServerSessionDestroy-, srp6ServerSessionStep1-, srp6ServerSessionStep2-, srp6GenerateVerifier-, srp6ClientAgree-, srp6GroupSize---- * SRP6 Types--, SRP6Verifier(..)-, SRP6BValue(..)-, SRP6AValue(..)-, SRP6SharedSecret(..)+module Botan.Low.SRP6 (+ -- * Example usage+ -- $usage+ -- * Password hashing+ SRP6ServerSession(..)+ , withSRP6ServerSession+ , srp6ServerSessionInit+ , srp6ServerSessionDestroy+ , srp6ServerSessionStep1+ , srp6ServerSessionStep2+ , srp6GenerateVerifier+ , srp6ClientAgree+ , srp6GroupSize+ -- * SRP6 Types+ , SRP6Verifier+ , SRP6BValue+ , SRP6AValue+ , SRP6SharedSecret+ -- * SRP discrete logarithm groups+ , pattern MODP_SRP_1024+ , pattern MODP_SRP_1536+ , pattern MODP_SRP_2048+ , pattern MODP_SRP_3072+ , pattern MODP_SRP_4096+ , pattern MODP_SRP_6144+ , pattern MODP_SRP_8192+ ) where --- * SRP discrete logarithm groups+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.SRP6+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.PubKey+import Botan.Low.Remake+import Botan.Low.RNG+import Data.ByteString (ByteString)+import qualified Data.ByteString.Internal as BS+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -, pattern MODP_SRP_1024-, pattern MODP_SRP_1536-, pattern MODP_SRP_2048-, pattern MODP_SRP_3072-, pattern MODP_SRP_4096-, pattern MODP_SRP_6144-, pattern MODP_SRP_8192+{-------------------------------------------------------------------------------+ Example usage+-------------------------------------------------------------------------------} -) where+-- NOTE: when changing this example, also change the copied example in the+-- "Test.Botan.Low.SRP6.Example" module. -import qualified Data.ByteString as ByteString+{- $usage -import Botan.Bindings.SRP6+This is a simplified, executable example that should give you an idea of how to+use the functions in this module to run the SRP6 protocol. We will not explain+the protocol in detail here, so consult other sources if you need details about+the algorithm. The simplified example shows how a client and server run the+protocol from separate threads, keeping private values secret and communicating+public values using message passing. In practice, the client and server will+probably run on separate machines (or processes) with secure communication+channels in place, and other security measures are necessary. So, don't use this+example directly in your code if it runs in production. -import Botan.Low.Error-import Botan.Low.Hash-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake-import Botan.Low.RNG-import Botan.Low.PubKey+Let us start with some necessary imports. -{- $introduction+>>> :{+{-# OPTIONS_GHC -threaded -Wall #-}+{-# LANGUAGE OverloadedStrings #-}+import Botan.Low.Hash+import Botan.Low.PubKey+import Botan.Low.RNG+import Botan.Low.SRP6+import Control.Concurrent+import Control.Concurrent.Async+import Data.ByteString hiding (group)+import Data.IORef+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe+import Text.Printf+:} -A SRP client provides what is called a SRP verifier to the server.-This verifier is based on a password, but the password cannot be-easily derived from the verifier (however brute force attacks are-possible). Later, the client and server can perform an SRP exchange,-which results in a shared secret key. This key can be used for-mutual authentication and/or encryption.+The client and server exchange a number of messages during the protocol. They+will be explained in more detail later as they become relevant for the part of+the protocol that we are discussing. The messages will be exchanged from the+client to the server using an @MVar ClientMsg@, and from the server to the+client using an @MVar ServerMsg@. -SRP works in a discrete logarithm group. Special parameter sets for-SRP6 are defined, denoted in the library as @modp\/srp\/<size>@, for-example @modp\/srp\/2048@.+>>> :{+type Identifier = ByteString+data ClientMsg =+ Signup Identifier SRP6Verifier+ | StartAuthentication Identifier+ | SendClientPublicKey SRP6AValue+data ServerMsg =+ SendServerPublicKey SRP6BValue+:} -Warning+SRP works in a discrete logarithm group, and it computes hashes using a hash+family. Both can be configured, and we will configure them as follows: -While knowledge of the verifier does not easily allow an attacker to-get the raw password, they could still use the verifier to impersonate-the server to the client, so verifiers should be protected as carefully-as a plaintext password would be.+>>> :{+group :: DLGroupName+group = MODP_SRP_4096+hash :: HashName+hash = SHA512+:} -SRP6 may be used as part of SSL/TLS: https://www.rfc-editor.org/rfc/rfc5054+When a client wants to sign up with the server with a combination of an+identifier and password, the client first generates a verifier using the+identifier, the password, and a locally generated salt. Then, the client sends+their identifier and verifier to the server, while it stores its own salt+locally: --}+>>> :{+type Password = ByteString+type Salt = ByteString+clientSignup :: MVar ClientMsg -> Identifier -> Password -> IO Salt+clientSignup clientMsgVar identifier password = do+ rng <- rngInit UserRNG+ salt <- rngGet rng 12+ verifier <- srp6GenerateVerifier identifier password salt group hash+ putMVar clientMsgVar $ Signup identifier verifier+ pure salt+:} -{- $usage+The server receives the signup request, and stores the identifier with the+verifier: -On signup, the client generates a salt and verifier, and securely sends them to a server:+>>> :{+type Users = Map Identifier SRP6Verifier+serverSignup :: MVar ClientMsg -> IORef Users -> IO ()+serverSignup clientMsgVar usersRef = do+ Signup identifier verifier <- takeMVar clientMsgVar+ modifyIORef usersRef $ Map.insert identifier verifier+:} -> import Botan.Low.SRP6-> import Botan.Low.Hash-> import Botan.Low.RNG-> import Botan.Low.MAC-> rng <- rngInit UserRNG-> group = MODP_SRP_4096-> hash = SHA512-> identifier = "alice"-> password = "Fee fi fo fum!"-> salt <- rngGet rng 12-> verifier <- srp6GenerateVerifier identifier password salt group hash-> -- signUpUserWithServer identifier verifier salt group hash+Later, the client may request to authenticate itself to the server: -Later, on the server when the client request authentication, the server-looks up the verfier, generates a server key (a SRP6 'B' value), and sends-it back to the client:+>>> :{+clientStartAuthentication :: MVar ClientMsg -> Identifier -> IO ()+clientStartAuthentication clientMsgVar identifier = do+ putMVar clientMsgVar $ StartAuthentication identifier+:} -> -- rng <- rngInit UserRNG-> session <- srp6ServerSessionInit-> -- (verifier, salt, group, hash) <- lookupUser identifier-> serverKey <- srp6ServerSessionStep1 session verifier group hash rng+The server receives the authentication request, looks up the verfier, generates+the server's public key (a SRP6 @B@ value), and sends it back to the client: -Once the client receives the server key, it generates a client key (SRP6 'A' value)-and the session key, and sends the client key to the server:+>>> :{+serverStartAuthentication :: MVar ClientMsg -> MVar ServerMsg -> IORef Users -> IO SRP6ServerSession+serverStartAuthentication clientMsgVar serverMsg usersRef = do+ StartAuthentication identifier <- takeMVar clientMsgVar+ users <- readIORef usersRef+ let verifier = fromJust $ Map.lookup identifier users+ rng <- rngInit UserRNG+ session <- srp6ServerSessionInit+ serverPublicKey <- srp6ServerSessionStep1 session verifier group hash rng+ putMVar serverMsg $ SendServerPublicKey serverPublicKey+ pure session+:} -> -- serverKey <- didReceiveServerKey-> (clientKey, clientSessionKey) <- srp6ClientAgree identifier password group hash salt serverKey rng-> -- sendClientKey clientKey+Once the client receives the server's public key, it generates the client's+public key (an SRP6 @B@ value) and the shared secret key, and sends the client's+public key to the server: -The server then receives client key, and generates a matching session key:+>>> :{+clientAuthentication :: MVar ClientMsg -> MVar ServerMsg -> Identifier -> Password -> Salt -> IO SRP6SharedSecret+clientAuthentication clientMsgVar serverMsgVar identifier password salt = do+ SendServerPublicKey serverPublicKey <- takeMVar serverMsgVar+ rng <- rngInit UserRNG+ (clientPublicKey, clientSecretKey) <-+ srp6ClientAgree identifier password group hash salt serverPublicKey rng+ putMVar clientMsgVar $ SendClientPublicKey clientPublicKey+ pure clientSecretKey+:} -> -- clientKey <- didReceiveClientKey-> serverSessionKey <- srp6ServerSessionStep2 session clientKey+The server then receives the client's public key, and generates a matching+shared secret key: -At this point, clientSessionKey and serverSessionKey should be equal,-but this should be confirmed by exchanging a hash digest to check for integrity,-using the exchange's session key, identifier, salt, and client and server keys.+>>> :{+serverAuthentication :: MVar ClientMsg -> SRP6ServerSession -> IO SRP6SharedSecret+serverAuthentication clientMsgVar session = do+ SendClientPublicKey clientPublicKey <- takeMVar clientMsgVar+ serverSecretKey <- srp6ServerSessionStep2 session group clientPublicKey+ pure serverSecretKey+:} -There are many ways to do this, but preferrably, an (h)mac digest should be used-to also include authentication and avoid impersonation.+At this point, the client's and servers' secret key should be equal. In+practice, the secret keys should be confirmed to match by exchanging a hash+digest of relevant protocol values, susch as the secret key, public keys,+identifier, salt, and more. Calculation of this hash digest is not part of this+example. Instead, we will return the secret keys from the client and server so+that we can compare them, which obviously breaks security, but this should be+fine for our example. -> NOTE: Both sides could calculate 'identifier <> salt <> serverKey <> clientKey'-> individually but then we need to prove that each side has calculated it without-relying on the copy received for validation, so we do this song and dance:+We bring the client-side of the algorithm together by picking an identifier and+password, and calling the client-side functions in order. -The client should first calculate and send the HMAC auth, using identifier + salt + clientKey:+>>> :{+client :: MVar ClientMsg -> MVar ServerMsg -> IO SRP6SharedSecret+client clientMsgVar serverMsgVar = do+ let identifier = "alice"+ password = "Fee fi fo fum!"+ salt <- clientSignup clientMsgVar identifier password+ clientStartAuthentication clientMsgVar identifier+ clientAuthentication clientMsgVar serverMsgVar identifier password salt+:} -> mac <- macInit (hmac SHA3)-> macSetKey mac clientSessionKey-> macUpdate mac $ identifier <> salt <> clientKey-> clientAuth <- macFinal mac-> -- sendClientAuth clientAuth+We bring the server-side of the algorithm together by initialising a session and+calling the server-side functions in order. -The server should then verify the client auth, and send its own HMAC-auth back to the client using serverKey + clientAuth:+>>> :{+server :: MVar ClientMsg -> MVar ServerMsg -> IO SRP6SharedSecret+server clientMsgVar serverMsgVar = do+ usersRef <- newIORef Map.empty+ serverSignup clientMsgVar usersRef+ session <- serverStartAuthentication clientMsgVar serverMsgVar usersRef+ serverAuthentication clientMsgVar session+:} -> -- clientAuth <- didReceiveClientAuth-> mac <- macInit (hmac SHA3)-> macSetKey mac serverSessionKey-> macUpdate mac $ identifier <> salt <> clientKey-> verifiedClientAuth <- macFinal mac-> -- clientAuth == verifiedClientAuth-> macClear mac-> macSetKey mac serverSessionKey-> macUpdate mac $ serverKey <> clientAuth-> serverAuth <- macFinal mac-> -- sendServerAuth serverAuth+Using the @async@ package, we spin up a thread for the client and a thread for+the server. We wait for them to finish, and then compare the resulting secret+keys. -The client then receives the server HMAC auth, and validates it+>>> :{+main :: IO ()+main = do+ clientMsgVar <- newEmptyMVar+ serverMsgVar <- newEmptyMVar+ (clientSecretKey, serverSecretKey) <-+ concurrently (client clientMsgVar serverMsgVar) (server clientMsgVar serverMsgVar)+ let match = clientSecretKey == serverSecretKey+ if match then+ print @String "client secret == server secret"+ else+ error $+ printf "%s /= %s"+ (show clientSecretKey)+ (show serverSecretKey)+:} -> -- serverAuth <- didReceiveServerAuth-> macClear mac-> macSetKey mac clientSessionKey-> macUpdate mac $ serverKey <> clientAuth-> verifiedServerAuth <- macFinal mac-> -- serverAuth == verifiedServerAuth+All that is left to do is run the program. -After this, the shared session key may be safely used.+>>> main+"client secret == server secret" -} +{-------------------------------------------------------------------------------+ Password hashing+-------------------------------------------------------------------------------} -- TODO: Unify with other / move to botan type Identifier = ByteString@@ -185,150 +278,218 @@ newtype SRP6ServerSession = MkSRP6ServerSession { getSRP6ServerSessionForeignPtr :: ForeignPtr BotanSRP6ServerSessionStruct } -newSRP6ServerSession :: BotanSRP6ServerSession -> IO SRP6ServerSession withSRP6ServerSession :: SRP6ServerSession -> (BotanSRP6ServerSession -> IO a) -> IO a--- | Destroy a SRP6 server session object immediately+-- | Frees all resources of the SRP-6 server session object srp6ServerSessionDestroy :: SRP6ServerSession -> IO () createSRP6ServerSession :: (Ptr BotanSRP6ServerSession -> IO CInt) -> IO SRP6ServerSession-(newSRP6ServerSession, withSRP6ServerSession, srp6ServerSessionDestroy, createSRP6ServerSession, _)+(withSRP6ServerSession, srp6ServerSessionDestroy, createSRP6ServerSession) = mkBindings- MkBotanSRP6ServerSession runBotanSRP6ServerSession- MkSRP6ServerSession getSRP6ServerSessionForeignPtr+ MkBotanSRP6ServerSession (.runBotanSRP6ServerSession)+ MkSRP6ServerSession (.getSRP6ServerSessionForeignPtr) botan_srp6_server_session_destroy -- | Initialize an SRP-6 server session object-srp6ServerSessionInit- :: IO SRP6ServerSession -- ^ __srp6__: SRP-6 server session object+srp6ServerSessionInit ::+ IO SRP6ServerSession -- ^ __srp6__: SRP-6 server session object srp6ServerSessionInit = createSRP6ServerSession botan_srp6_server_session_init --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withSRP6ServerSessionInit :: (SRP6ServerSession -> IO a) -> IO a-withSRP6ServerSessionInit = mkWithTemp srp6ServerSessionInit srp6ServerSessionDestroy---- | SRP-6 Server side step 1: Generate a server B-value-srp6ServerSessionStep1- :: SRP6ServerSession -- ^ __srp6__: SRP-6 server session object- -> SRP6Verifier -- ^ __verifier[]__: the verification value saved from client registration- -> DLGroupName -- ^ __group_id__: the SRP group id- -> HashName -- ^ __hash_id__: the SRP hash in use- -> RNG -- ^ __rng_obj__: a random number generator object- -> IO SRP6BValue -- ^ __B_pub[]__: out buffer to store the SRP-6 B value-srp6ServerSessionStep1 srp6 verifier groupId hashId rng = withSRP6ServerSession srp6 $ \ srp6Ptr -> do- asBytesLen verifier $ \ verifierPtr verifierLen -> do- asCString groupId $ \ groupIdPtr -> do- asCString hashId $ \ hashIdPtr -> do- withRNG rng $ \ botanRNG -> do- allocBytesQuerying $ \ outPtr outLen -> botan_srp6_server_session_step1- srp6Ptr- (ConstPtr verifierPtr)- verifierLen- (ConstPtr groupIdPtr)- (ConstPtr hashIdPtr)- botanRNG- outPtr- outLen---- | SRP-6 Server side step 2: Generate the server shared key-srp6ServerSessionStep2- :: SRP6ServerSession -- ^ __srp6__: SRP-6 server session object- -> SRP6AValue -- ^ __A[]__: the client's value- -> IO SRP6SharedSecret -- ^ __key[]__: out buffer to store the symmetric key value-srp6ServerSessionStep2 srp6 a = withSRP6ServerSession srp6 $ \ srp6Ptr -> do- asBytesLen a $ \ aPtr aLen -> do- allocBytesQuerying $ \ outPtr outLen -> botan_srp6_server_session_step2- srp6Ptr- (ConstPtr aPtr)- aLen- outPtr- outLen+-- | SRP-6 Server side step 1+--+-- NOTE: this function should be not be invoked twice on the same server+-- session. Regardless of the result of the first invocation, the second+-- invocation will result in an error. See+-- https://github.com/randombit/botan/issues/5112 for more information. If a+-- second invocation can not be prevented, try it on a newly initialised server+-- session instead.+srp6ServerSessionStep1 ::+ SRP6ServerSession -- ^ __srp6__: SRP-6 server session object+ -> SRP6Verifier -- ^ __verifier[]__: the verification value saved from client registration+ -> DLGroupName -- ^ __group_id__: the SRP group id+ -> HashName -- ^ __hash_id__: the SRP hash in use+ -> RNG -- ^ __rng_obj__: a random number generator object+ -> IO SRP6BValue -- ^ __B_pub[]__: out buffer to store the SRP-6 B value+srp6ServerSessionStep1 srp6 verifier groupId hashId rng =+ withSRP6ServerSession srp6 $ \ srp6Ptr ->+ asBytesLen verifier $ \ verifierPtr verifierLen ->+ asCString groupId $ \ groupIdPtr ->+ asCString hashId $ \ hashIdPtr ->+ withRNG rng $ \ botanRNG ->+ createWithGroupSize groupId $ \ outPtr outLen ->+ throwBotanIfNegative_ $+ botan_srp6_server_session_step1+ srp6Ptr+ (ConstPtr verifierPtr)+ verifierLen+ (ConstPtr groupIdPtr)+ (ConstPtr hashIdPtr)+ botanRNG+ outPtr+ outLen --- | SRP-6 Client side step 1: Generate a new SRP-6 verifier-srp6GenerateVerifier- :: Identifier -- ^ __identifier__: a username or other client identifier- -> Password -- ^ __password__: the secret used to authenticate user- -> Salt -- ^ __salt[]__: a randomly chosen value, at least 128 bits long- -> DLGroupName -- ^ __group_id__: specifies the shared SRP group- -> HashName -- ^ __hash_id__: specifies a secure hash function- -> IO SRP6Verifier -- ^ __verifier[]__: out buffer to store the SRP-6 verifier value-srp6GenerateVerifier identifier password salt groupId hashId = asCString identifier $ \ identifierPtr -> do- asCString password $ \ passwordPtr -> do- asBytesLen salt $ \ saltPtr saltLen -> do- asCString groupId $ \ groupIdPtr -> do- asCString hashId $ \ hashIdPtr -> do- allocBytesQuerying $ \ outPtr outLen -> botan_srp6_generate_verifier- (ConstPtr identifierPtr)- (ConstPtr passwordPtr)- (ConstPtr saltPtr)- saltLen- (ConstPtr groupIdPtr)- (ConstPtr hashIdPtr)- outPtr- outLen+-- | SRP-6 Server side step 2+srp6ServerSessionStep2 ::+ SRP6ServerSession -- ^ __srp6__: SRP-6 server session object+ -> SRP6AValue -- ^ __A[]__: the client's value+ -> DLGroupName -- ^ the SRP group id+ -> IO SRP6SharedSecret -- ^ __key[]__: out buffer to store the symmetric key value+srp6ServerSessionStep2 srp6 groupId a =+ withSRP6ServerSession srp6 $ \ srp6Ptr ->+ asBytesLen a $ \ aPtr aLen ->+ createWithGroupSize groupId $ \ outPtr outLen ->+ throwBotanIfNegative_ $+ botan_srp6_server_session_step2+ srp6Ptr+ (ConstPtr aPtr)+ aLen+ outPtr+ outLen --- NOTE: ORDER IS DIFFERENT FROM SERVER GENERATE VERIFIER--- | SRP6a Client side step 2: Generate a client A-value and the client shared key-srp6ClientAgree- :: Identifier -- ^ __username__: the username we are attempting login for- -> Password -- ^ __password__: the password we are attempting to use- -> DLGroupName -- ^ __group_id__: specifies the shared SRP group- -> HashName -- ^ __hash_id__: specifies a secure hash function- -> Salt -- ^ __salt[]__: is the salt value sent by the server- -> SRP6BValue -- ^ __uint8_t__: B[] is the server's public value- -> RNG -- ^ __rng_obj__: is a random number generator object- -> IO (SRP6AValue, SRP6SharedSecret) -- @(A,K)@-srp6ClientAgree identifier password groupId hashId salt b rng = do- asCString identifier $ \ identifierPtr -> do- asCString password $ \ passwordPtr -> do- asCString groupId $ \ groupIdPtr -> do- asCString hashId $ \ hashIdPtr -> do- asBytesLen salt $ \ saltPtr saltLen -> do- asBytesLen b $ \ bPtr bLen -> do- withRNG rng $ \ botanRNG -> do- alloca $ \ aSzPtr -> do- alloca $ \ kSzPtr -> do- -- Query sizes- -- TODO: Actually ensure expected error (insufficient buffer space)- -- and propagate unexpected errors- _ <- botan_srp6_client_agree- (ConstPtr identifierPtr)- (ConstPtr passwordPtr)- (ConstPtr groupIdPtr)- (ConstPtr hashIdPtr)- (ConstPtr saltPtr)- saltLen- (ConstPtr bPtr)- bLen- botanRNG- nullPtr- aSzPtr- nullPtr- kSzPtr- kSz <- fromIntegral <$> peek kSzPtr- aSz <- fromIntegral <$> peek aSzPtr- allocBytesWith kSz $ \ kPtr -> do- allocBytes aSz $ \ aPtr -> do- throwBotanIfNegative_ $ botan_srp6_client_agree- (ConstPtr identifierPtr)- (ConstPtr passwordPtr)- (ConstPtr groupIdPtr)- (ConstPtr hashIdPtr)- (ConstPtr saltPtr)- saltLen- (ConstPtr bPtr)- bLen- botanRNG- aPtr- aSzPtr- kPtr- kSzPtr+-- | Generate a new SRP-6 verifier+srp6GenerateVerifier ::+ Identifier -- ^ __identifier__: a username or other client identifier+ -> Password -- ^ __password__: the secret used to authenticate user+ -> Salt -- ^ __salt[]__: a randomly chosen value, at least 128 bits long+ -> DLGroupName -- ^ __group_id__: specifies the shared SRP group+ -> HashName -- ^ __hash_id__: specifies a secure hash function+ -> IO SRP6Verifier -- ^ __verifier[]__: out buffer to store the SRP-6 verifier value+srp6GenerateVerifier identifier password salt groupId hashId =+ asCString identifier $ \ identifierPtr ->+ asCString password $ \ passwordPtr ->+ asBytesLen salt $ \ saltPtr saltLen ->+ asCString groupId $ \ groupIdPtr ->+ asCString hashId $ \ hashIdPtr ->+ createWithGroupSize groupId $ \ outPtr outLen ->+ throwBotanIfNegative_ $+ botan_srp6_generate_verifier+ (ConstPtr identifierPtr)+ (ConstPtr passwordPtr)+ (ConstPtr saltPtr)+ saltLen+ (ConstPtr groupIdPtr)+ (ConstPtr hashIdPtr)+ outPtr+ (castPtr outLen) --- NOTE: Missing FFI function: srp6_group_identifierz+-- | SRP6a Client side+srp6ClientAgree ::+ -- | __username__: the username we are attempting login for+ Identifier+ -- | __password__: the password we are attempting to use+ -> Password+ -- | __group_id__: specifies the shared SRP group+ -> DLGroupName+ -- | __hash_id__: specifies a secure hash function+ -> HashName+ -- | __salt[]__: is the salt value sent by the server+ -> Salt+ -- | __B[]__: is the server's public value+ -> SRP6BValue+ -- | __rng_obj__: is a random number generator object+ -> RNG+ -- | A tuple of two elements (in order):+ --+ -- * __A[]__: out buffer to store the SRP-6 A value+ -- * __K[]__: out buffer to store the symmetric value+ -> IO (SRP6AValue, SRP6SharedSecret)+srp6ClientAgree identifier password groupId hashId salt b rng =+ asCString identifier $ \ identifierPtr ->+ asCString password $ \ passwordPtr ->+ asCString groupId $ \ groupIdPtr ->+ asCString hashId $ \ hashIdPtr ->+ asBytesLen salt $ \ saltPtr saltLen ->+ asBytesLen b $ \ bPtr bLen ->+ withRNG rng $ \ botanRNG ->+ createWithGroupSize' groupId $ \ aPtr aSzPtr ->+ createWithGroupSize groupId $ \ kPtr kSzPtr ->+ throwBotanIfNegative_ $+ botan_srp6_client_agree+ (ConstPtr identifierPtr)+ (ConstPtr passwordPtr)+ (ConstPtr groupIdPtr)+ (ConstPtr hashIdPtr)+ (ConstPtr saltPtr)+ saltLen+ (ConstPtr bPtr)+ bLen+ botanRNG+ aPtr+ aSzPtr+ kPtr+ kSzPtr -- | Return the size, in bytes, of the prime associated with group_id-srp6GroupSize- :: DLGroupName -- ^ __group_id__- -> IO Int -- ^ __group_p_bytes__-srp6GroupSize groupId = asCString groupId $ \ groupIdPtr -> do+--+-- This function can be used to determine the size of output buffers for+-- generated keys in the SRP6 algorithm. Such buffers need to be allocated+-- before calling SRP6 functions. An example of such a buffer is the+-- @verifier[]@ buffer in the 'srp6GenerateVerifier' function. In @botan-low@,+-- such buffers are created internally using 'srp6GroupSize', without requiring+-- input from you (the user).+srp6GroupSize ::+ DLGroupName -- ^ __group_id__+ -> IO Int -- ^ __group_p_bytes__+srp6GroupSize groupId =+ asCString groupId $ \ groupIdPtr -> alloca $ \ szPtr -> do- throwBotanIfNegative_ $ botan_srp6_group_size (ConstPtr groupIdPtr) szPtr- fromIntegral <$> peek szPtr+ throwBotanIfNegative_ $+ botan_srp6_group_size (ConstPtr groupIdPtr) szPtr+ fromIntegral <$> peek szPtr++{-------------------------------------------------------------------------------+ Utility+-------------------------------------------------------------------------------}++-- | Like 'createWithSize', but we determine the maximum size of the byte string+-- based on an argument 'DLGroupName'.+createWithGroupSize ::+ DLGroupName+ -> (Ptr Word8 -> Ptr CSize -> IO ())+ -> IO ByteString+createWithGroupSize groupId k = do+ sz <- srp6GroupSize groupId+ createWithSize sz k++-- | Given the maximum size needed and a function to make the contents of a+-- 'ByteString', 'createWithSize' makes the 'ByteString'.+--+-- The generating function is required to write the actual final size (<= the+-- maximum size) to the 'CSize' pointer, and the resulting byte array is+-- reallocated to this size.+--+-- NOTE: this is based on 'BS.createAndTrim'.+createWithSize ::+ Int+ -> (Ptr Word8 -> Ptr CSize -> IO ())+ -> IO ByteString+createWithSize sz k =+ BS.createAndTrim sz $ \bytesPtr ->+ alloca $ \lenPtr -> do+ poke lenPtr (fromIntegral sz)+ k bytesPtr lenPtr+ fromIntegral <$> peek lenPtr++-- | Like 'createWithGroupSize', but also returns an additional value created by+-- the action.+createWithGroupSize' ::+ DLGroupName+ -> (Ptr Word8 -> Ptr CSize -> IO a)+ -> IO (ByteString, a)+createWithGroupSize' groupId k = do+ sz <- srp6GroupSize groupId+ createWithSize' sz k++-- | Like 'createWithSize', but also returns an additional value created by the+-- action.+--+-- NOTE: this is based on 'BS.createAndTrim''.+createWithSize' ::+ Int+ -> (Ptr Word8 -> Ptr CSize -> IO a)+ -> IO (ByteString, a)+createWithSize' sz k =+ BS.createAndTrim' sz $ \bytesPtr ->+ alloca $ \lenPtr -> do+ poke lenPtr (fromIntegral sz)+ x <- k bytesPtr lenPtr+ sz' <- fromIntegral <$> peek lenPtr+ pure (0, sz', x)
src/Botan/Low/TOTP.hs view
@@ -18,48 +18,52 @@ -} -module Botan.Low.TOTP-(---- * Time-based one time passwords--- $introduction--- * Usage--- $usage---- * TOTP+module Botan.Low.TOTP ( - TOTP(..)-, TOTPHashName(..)-, TOTPTimestep(..)-, TOTPTimestamp(..)-, TOTPCode(..)-, withTOTP-, totpInit-, totpDestroy-, totpGenerate-, totpCheck+ -- * Time-based one time passwords+ -- $introduction+ -- * Usage+ -- $usage --- * TOTP Hashes+ -- * TOTP -, pattern TOTP_SHA1-, pattern TOTP_SHA256-, pattern TOTP_SHA512+ TOTP(..)+ , TOTPHashName+ , TOTPTimestep+ , TOTPTimestamp+ , TOTPCode+ , withTOTP+ , totpInit+ , totpDestroy+ , totpGenerate+ , totpCheck --- * Convenience+ -- * TOTP Hashes -, totpHashes+ , pattern TOTP_SHA1+ , pattern TOTP_SHA256+ , pattern TOTP_SHA512 -) where+ -- * Convenience -import qualified Data.ByteString as ByteString+ , totpHashes -import Botan.Bindings.TOTP+ ) where -import Botan.Low.Error-import Botan.Low.Hash-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.Remake+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.TOTP+import Botan.Low.Error.Internal+import Botan.Low.Hash+import Botan.Low.Internal.ByteString+import Botan.Low.Remake+import Control.Monad+import Data.ByteString (ByteString)+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable -- NOTE: RFC 6238 @@ -158,14 +162,13 @@ newtype TOTP = MkTOTP { getTOTPForeignPtr :: ForeignPtr BotanTOTPStruct } -newTOTP :: BotanTOTP -> IO TOTP withTOTP :: TOTP -> (BotanTOTP -> IO a) -> IO a totpDestroy :: TOTP -> IO () createTOTP :: (Ptr BotanTOTP -> IO CInt) -> IO TOTP-(newTOTP, withTOTP, totpDestroy, createTOTP, _)+(withTOTP, totpDestroy, createTOTP) = mkBindings- MkBotanTOTP runBotanTOTP- MkTOTP getTOTPForeignPtr+ MkBotanTOTP (.runBotanTOTP)+ MkTOTP (.getTOTPForeignPtr) botan_totp_destroy type TOTPHashName = HashName@@ -180,6 +183,7 @@ pattern TOTP_SHA512 = SHA512 -- TODO: Do any other hashes work?+totpHashes :: [TOTPHashName] totpHashes = [ TOTP_SHA1 , TOTP_SHA256@@ -209,10 +213,6 @@ (fromIntegral digits) (fromIntegral timestep) --- WARNING: withFooInit-style limited lifetime functions moved to high-level botan-withTOTPInit :: ByteString -> ByteString -> Int -> TOTPTimestep -> (TOTP -> IO a) -> IO a-withTOTPInit = mkWithTemp4 totpInit totpDestroy- -- | Generate a TOTP code for the provided timestamp totpGenerate :: TOTP -- ^ __totp__: the TOTP object@@ -220,16 +220,17 @@ -> IO TOTPCode -- ^ __timestamp__: the current local timestamp totpGenerate totp timestamp = withTOTP totp $ \ totpPtr -> do alloca $ \ outPtr -> do- throwBotanIfNegative $ botan_totp_generate totpPtr outPtr timestamp+ void $ throwBotanIfNegative $ botan_totp_generate totpPtr outPtr timestamp peek outPtr -- | Verify a TOTP code-totpCheck- :: TOTP -- ^ __totp__: the TOTP object- -> TOTPCode -- ^ __totp_code__: the presented OTP- -> TOTPTimestamp -- ^ __timestamp__: the current local timestamp- -> Int -- ^ __acceptable_clock_drift__: specifies the acceptable amount- -- of clock drift (in terms of time steps) between the two hosts.- -> IO Bool-totpCheck totp code timestamp drift = withTOTP totp $ \ totpPtr -> do- throwBotanCatchingSuccess $ botan_totp_check totpPtr code timestamp (fromIntegral drift)+totpCheck ::+ TOTP -- ^ __totp__: the TOTP object+ -> TOTPCode -- ^ __totp_code__: the presented OTP+ -> TOTPTimestamp -- ^ __timestamp__: the current local timestamp+ -> Int -- ^ __acceptable_clock_drift__: specifies the acceptable amount+ -- of clock drift (in terms of time steps) between the two hosts.+ -> IO Bool+totpCheck totp code timestamp drift =+ withTOTP totp $ \ totpPtr ->+ throwBotanCatchingInvalidVerifier $ botan_totp_check totpPtr code timestamp (fromIntegral drift)
src/Botan/Low/Utility.hs view
@@ -7,123 +7,111 @@ Maintainer : joris@well-typed.com, leo@apotheca.io Stability : experimental Portability : POSIX--} -module Botan.Low.Utility-( constantTimeCompare-, scrubMem-, HexEncodingFlags-, pattern HexUpperCase-, pattern HexLowerCase-, hexEncode-, hexDecode-, base64Encode-, base64Decode-) where+This module is based on the [Utility+Functions](https://botan.randombit.net/handbook/api_ref/ffi.html#utility-functions)+section of the C Botan FFI documentation.+-} -import Data.Bool+module Botan.Low.Utility (+ constantTimeCompare+ , scrubMem+ , HexEncodingFlags(..)+ , hexEncode+ , hexDecode+ , base64Encode+ , base64Decode+ ) where -import qualified Data.Text as Text+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.Utility+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Data.ByteString (ByteString)+import Data.Text import qualified Data.Text.Encoding as Text--import qualified Data.ByteString as ByteString--import System.IO.Unsafe--import Botan.Bindings.Utility--import Botan.Low.Error-import Botan.Low.Prelude-import Botan.Low.Make (allocBytesQuerying, allocBytesQueryingCString)---- NOTE: Use of Text is unique here - leave for Text for `botan`+import Data.Word+import Foreign.Ptr -- | Returns 0 if x[0..len] == y[0..len], -1 otherwise.-constantTimeCompare- :: ByteString -- ^ __x__- -> ByteString -- ^ __y__- -> Int -- ^ __len__- -> IO Bool-constantTimeCompare x y len = do- asBytesLen x $ \ xPtr xlen -> do- asBytesLen y $ \ yPtr ylen -> do- result <- botan_constant_time_compare- (ConstPtr xPtr)- (ConstPtr yPtr)- xlen- case result of- 0 -> return True- _ -> return False+constantTimeCompare ::+ ByteString -- ^ __x__+ -> ByteString -- ^ __y__+ -> Int -- ^ __len__+ -> IO Bool+constantTimeCompare x y len =+ asBytesLen x $ \ xPtr _xlen ->+ asBytesLen y $ \ yPtr _ylen -> do+ result <- botan_constant_time_compare+ (ConstPtr xPtr)+ (ConstPtr yPtr)+ (fromIntegral len)+ case result of+ 0 -> return True+ _ -> return False -scrubMem- :: Ptr a -- ^ __mem__- -> Int -- ^ __bytes__- -> IO ()+scrubMem ::+ Ptr a -- ^ __mem__+ -> Int -- ^ __bytes__+ -> IO () scrubMem ptr sz = throwBotanIfNegative_ $ botan_scrub_mem (castPtr ptr) (fromIntegral sz) -type HexEncodingFlags = Word32--pattern HexUpperCase -- ^ NOTE: Not an actual flag- , HexLowerCase- :: HexEncodingFlags+data HexEncodingFlags =+ HexUpperCase+ | HexLowerCase -pattern HexUpperCase = BOTAN_FFI_HEX_UPPER_CASE-pattern HexLowerCase = BOTAN_FFI_HEX_LOWER_CASE+hexEncodingFlags :: HexEncodingFlags -> Word32+hexEncodingFlags HexUpperCase = BOTAN_FFI_HEX_UPPER_CASE+hexEncodingFlags HexLowerCase = BOTAN_FFI_HEX_LOWER_CASE --- | Performs hex encoding of binary data in x of size len bytes. The output buffer out must be of at least x*2 bytes in size. If flags contains BOTAN_FFI_HEX_LOWER_CASE, hex encoding will only contain lower-case letters, upper-case letters otherwise. Returns 0 on success, 1 otherwise.--- DISCUSS: Handling of positive return code / BOTAN_FFI_INVALID_VERIFIER?--- DISCUSS: Use of Text.decodeUtf8 - bad, partial function! - but safe here?-hexEncode- :: ByteString -- ^ __x__- -> HexEncodingFlags -- ^ __flags__- -> IO Text -- ^ __y__-hexEncode bytes flags = Text.decodeUtf8 <$> do- asBytesLen bytes $ \ bytesPtr bytesLen -> do- allocBytes (fromIntegral $ 2 * bytesLen) $ \ hexPtr -> do- throwBotanIfNegative_ $ botan_hex_encode- (ConstPtr bytesPtr)- bytesLen- hexPtr- flags+hexEncode ::+ ByteString -- ^ __x__+ -> HexEncodingFlags -- ^ __flags__+ -> IO Text -- ^ __y__+hexEncode bytes flags =+ fmap Text.decodeUtf8 $+ asBytesLen bytes $ \ bytesPtr bytesLen ->+ allocBytes (fromIntegral $ 2 * bytesLen) $ \ hexPtr ->+ throwBotanIfNegative_ $ botan_hex_encode+ (ConstPtr bytesPtr)+ bytesLen+ hexPtr+ (hexEncodingFlags flags) --- | "Hex decode some data"--- DISCUSS: Return value, maybe vs exception--- DISCUSS: Botan documentation is lacking here--- WARNING: Does not actually check that len is a multiple of 2-hexDecode- :: Text -- ^ __hex_str__- -> IO ByteString -- ^ __out__-hexDecode txt = do- asBytesLen (Text.encodeUtf8 txt) $ \ hexPtr hexLen -> do- allocBytesQuerying $ \ bytesPtr szPtr -> do- botan_hex_decode- (ConstPtr hexPtr)- hexLen- bytesPtr- szPtr+hexDecode ::+ Text -- ^ __hex_str__+ -> IO ByteString -- ^ __out__+hexDecode txt =+ asBytesLen (Text.encodeUtf8 txt) $ \ hexPtr hexLen ->+ allocBytesQuerying $ \ bytesPtr szPtr ->+ botan_hex_decode+ (ConstPtr hexPtr)+ hexLen+ bytesPtr+ szPtr --- NOTE: Does not check tht base64Len == peek sizePtr-base64Encode- :: ByteString -- ^ __x__- -> IO Text -- ^ __out__-base64Encode bytes = Text.decodeUtf8 <$> do- asBytesLen bytes $ \ bytesPtr bytesLen -> do- allocBytesQueryingCString $ \ base64Ptr szPtr -> do- botan_base64_encode- (ConstPtr bytesPtr)- bytesLen- base64Ptr- szPtr+base64Encode ::+ ByteString -- ^ __x__+ -> IO Text -- ^ __out__+base64Encode bytes =+ fmap Text.decodeUtf8 $+ asBytesLen bytes $ \ bytesPtr bytesLen ->+ allocBytesQueryingCString $ \ base64Ptr szPtr ->+ botan_base64_encode+ (ConstPtr bytesPtr)+ bytesLen+ base64Ptr+ szPtr --- | Ditto everything hexDecode-base64Decode- :: Text -- ^ __base64_str__- -> IO ByteString -- ^ __out__-base64Decode txt = do- asBytesLen (Text.encodeUtf8 txt) $ \ base64Ptr base64Len -> do- allocBytesQueryingCString $ \ bytesPtr szPtr -> do- botan_base64_decode- (ConstPtr base64Ptr)- base64Len- bytesPtr- szPtr+base64Decode ::+ Text -- ^ __base64_str__+ -> IO ByteString -- ^ __out__+base64Decode txt =+ asBytesLen (Text.encodeUtf8 txt) $ \ base64Ptr base64Len ->+ allocBytesQueryingCString $ \ bytesPtr szPtr ->+ botan_base64_decode+ (ConstPtr base64Ptr)+ base64Len+ bytesPtr+ szPtr
src/Botan/Low/Version.hs view
@@ -7,52 +7,45 @@ Maintainer : joris@well-typed.com, leo@apotheca.io Stability : experimental Portability : POSIX--} -module Botan.Low.Version-( botanFFIAPIVersion-, botanFFISupportsAPI-, botanVersionString-, botanVersionMajor-, botanVersionMinor-, botanVersionPatch-, botanVersionDatestamp-) where--import Data.Bool--import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Unsafe as ByteString--import qualified Data.Text as Text-import qualified Data.Text.Foreign as Text--import System.IO.Unsafe--import Botan.Bindings.Version+This module is based on the+[Versioning](https://botan.randombit.net/handbook/api_ref/ffi.html#versioning)+section of the C Botan FFI documentation.+-} -import Botan.Low.Prelude-import Botan.Low.Error (throwBotanCatchingSuccess)-import GHC.Generics ((:.:)(unComp1))+module Botan.Low.Version (+ botanFFIAPIVersion+ , botanFFISupportsAPI+ , botanVersionString+ , botanVersionMajor+ , botanVersionMinor+ , botanVersionPatch+ , botanVersionDatestamp+ ) where --- https://botan.randombit.net/handbook/api_ref/ffi.html#versioning+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.Version+import Botan.Low.Error.Internal (throwBotanCatchingInvalidInput)+import Botan.Low.Internal.ByteString+import Data.ByteString (ByteString) --- | Returns the version of the currently supported FFI API. This is expressed in the form YYYYMMDD of the release date of this version of the API.+-- | Returns the version of the currently supported FFI API. This is expressed+-- in the form YYYYMMDD of the release date of this version of the API. botanFFIAPIVersion :: IO Int botanFFIAPIVersion = fromIntegral <$> botan_ffi_api_version --- | Returns 0 iff the FFI version specified is supported by this library. Otherwise returns -1. The expression botan_ffi_supports_api(botan_ffi_api_version()) will always evaluate to 0. A particular version of the library may also support other (older) versions of the FFI API.+-- | Returns 'True' iff the FFI version specified is supported by this library,+-- otherwise returns 'False'.+--+-- The expression @'botanFFIAPIVersion' >>= 'botanFFISupportsAPI'@ will always+-- evaluate to 'True'. A particular version of the library may also support+-- other (older) versions of the FFI API. botanFFISupportsAPI :: Int -> IO Bool botanFFISupportsAPI version = do- supports <- botan_ffi_supports_api $ fromIntegral version- case supports of- 0 -> return True- _ -> return False--- TODO: botanFFISupportsAPI = throwBotanCatchingSuccess . botan_ffi_supports_api . fromIntegral--- AFTER renaming current throwBotanCatchingSuccess to throwBotanCatchingInvalidIdentifier+ throwBotanCatchingInvalidInput $ botan_ffi_supports_api (fromIntegral version) botanVersionString :: IO ByteString-botanVersionString = botan_version_string >>= peekCString . unConstPtr+botanVersionString = botan_version_string >>= peekCString . (.unConstPtr) -- | Returns the major version of the library botanVersionMajor :: IO Int@@ -66,6 +59,7 @@ botanVersionPatch :: IO Int botanVersionPatch = fromIntegral <$> botan_version_patch --- | Returns the date this version was released as an integer YYYYMMDD, or 0 if an unreleased version+-- | Returns the date this version was released as an integer YYYYMMDD, or 0 if+-- an unreleased version botanVersionDatestamp :: IO Int botanVersionDatestamp = fromIntegral <$> botan_version_datestamp
src/Botan/Low/View.hs view
@@ -7,31 +7,25 @@ Maintainer : joris@well-typed.com, leo@apotheca.io Stability : experimental Portability : POSIX--} -module Botan.Low.View-(-- BotanViewBinFn(..)-, BotanViewBinCallback(..)-, viewBin-, BotanViewStrFn(..)-, BotanViewStrCallback(..)-, viewStr--)where--import qualified Data.ByteString as ByteString--import Data.Void--import System.IO+This module is based on the [View+Functions](https://botan.randombit.net/handbook/api_ref/ffi.html#view-functions)+section of the C Botan FFI documentation.+-} -import Botan.Bindings.View+module Botan.Low.View (+ -- * View binary+ BotanViewBinFn+ , BotanViewBinCallback+ , viewBin+ -- * View string+ , BotanViewStrFn+ , BotanViewStrCallback+ , viewStr+ ) where -import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude+import Botan.Bindings.View+import Control.Exception viewBin :: BotanViewBinFn ctx -> (BotanViewBinCallback ctx -> IO a) -> IO a viewBin f = bracket (mallocBotanViewBinCallback f) freeBotanViewBinCallback
src/Botan/Low/X509.hs view
@@ -19,82 +19,82 @@ instance in the Transport Layer Security (TLS) protocol. -} -module Botan.Low.X509-(+module Botan.Low.X509 ( --- * X509 Certificates+ -- * X509 Certificates - X509Cert(..)-, withX509Cert-, x509CertLoad-, x509CertLoadFile-, x509CertDestroy-, x509CertDup-, x509CertGetTimeStarts-, x509CertGetTimeExpires-, x509CertNotBefore-, x509CertNotAfter-, x509CertGetPubKeyFingerprint-, x509CertGetSerialNumber-, x509CertGetAuthorityKeyId-, x509CertGetSubjectKeyId-, x509CertGetPublicKeyBits-, x509CertGetPublicKey-, x509CertGetIssuerDN-, x509CertGetSubjectDN-, x509CertToString-, x509CertAllowedUsage-, x509CertHostnameMatch-, x509CertVerify-, x509CertValidationStatus+ X509Cert(..)+ , withX509Cert+ , x509CertLoad+ , x509CertLoadFile+ , x509CertDestroy+ , x509CertDup+ , x509CertGetTimeStarts+ , x509CertGetTimeExpires+ , x509CertNotBefore+ , x509CertNotAfter+ , x509CertGetPubKeyFingerprint+ , x509CertGetSerialNumber+ , x509CertGetAuthorityKeyId+ , x509CertGetSubjectKeyId+ , x509CertGetPublicKeyBits+ , x509CertGetPublicKey+ , x509CertGetIssuerDN+ , x509CertGetSubjectDN+ , x509CertToString+ , x509CertAllowedUsage+ , x509CertHostnameMatch+ , x509CertVerify+ , x509CertValidationStatus --- * X509 Key constraints+ -- * X509 Key constraints -, X509KeyConstraints(..)-, pattern NoConstraints-, pattern DigitalSignature-, pattern NonRepudiation-, pattern KeyEncipherment-, pattern DataEncipherment-, pattern KeyAgreement-, pattern KeyCertSign-, pattern CRLSign-, pattern EncipherOnly-, pattern DecipherOnly+ , X509KeyConstraints+ , pattern NoConstraints+ , pattern DigitalSignature+ , pattern NonRepudiation+ , pattern KeyEncipherment+ , pattern DataEncipherment+ , pattern KeyAgreement+ , pattern KeyCertSign+ , pattern CRLSign+ , pattern EncipherOnly+ , pattern DecipherOnly --- * X509 Certificate revocation list+ -- * X509 Certificate revocation list -, X509CRL(..)-, withX509CRL-, x509CRLLoad-, x509CRLLoadFile-, x509CRLDestroy-, x509IsRevoked-, x509CertVerifyWithCLR+ , X509CRL(..)+ , withX509CRL+ , x509CRLLoad+ , x509CRLLoadFile+ , x509CRLDestroy+ , x509IsRevoked+ , x509CertVerifyWithCLR --- * Convenience-, DistinguishedName(..)+ -- * Convenience+ , DistinguishedName -) where+ ) where -import qualified Data.ByteString as ByteString+import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.X509+import Botan.Low.Error.Internal+import Botan.Low.Hash (HashName)+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Botan.Low.PubKey+import Botan.Low.Remake+import Control.Monad+import Data.ByteString (ByteString, packCString) import qualified Data.ByteString.Char8 as Char8--import Botan.Bindings.PubKey-import Botan.Bindings.X509--import Botan.Low.Hash (HashName(..))--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.Remake-import Data.Maybe (fromMaybe)-import Data.ByteString (packCString)+import Data.Word import qualified Foreign.C.String as String (withCString)-import Botan.Low.Remake (mkCreateObjectCBytesLen)-import Botan.Low.PubKey (createPubKey)+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.Marshal.Alloc+import Foreign.Marshal.Array+import Foreign.Ptr+import Foreign.Storable -- TODO: Use *.Make module to ensure consistency @@ -106,13 +106,12 @@ newtype X509Cert = MkX509Cert { getX509CertForeignPtr :: ForeignPtr BotanX509CertStruct } -newX509Cert :: BotanX509Cert -> IO X509Cert withX509Cert :: X509Cert -> (BotanX509Cert -> IO a) -> IO a -- | Destroy an x509 cert object immediately x509CertDestroy :: X509Cert -> IO () createX509Cert :: (Ptr BotanX509Cert -> IO CInt) -> IO X509Cert-(newX509Cert, withX509Cert, x509CertDestroy, createX509Cert, _)- = mkBindings MkBotanX509Cert runBotanX509Cert MkX509Cert getX509CertForeignPtr botan_x509_cert_destroy+(withX509Cert, x509CertDestroy, createX509Cert)+ = mkBindings MkBotanX509Cert (.runBotanX509Cert) MkX509Cert (.getX509CertForeignPtr) botan_x509_cert_destroy x509CertLoad :: ByteString -- ^ __cert[]__@@ -145,10 +144,10 @@ -> IO Word64 -- ^ __time_since_epoch__ x509CertNotBefore cert = withX509Cert cert $ \ certPtr -> do alloca $ \ timePtr -> do- botan_x509_cert_not_before+ void $ botan_x509_cert_not_before certPtr timePtr- fromIntegral <$> peek timePtr+ peek timePtr -- TODO: mkGetIntegral x509CertNotAfter@@ -156,10 +155,10 @@ -> IO Word64 -- ^ __time_since_epoch__ x509CertNotAfter cert = withX509Cert cert $ \ certPtr -> do alloca $ \ timePtr -> do- botan_x509_cert_not_after+ void $ botan_x509_cert_not_after certPtr timePtr- fromIntegral <$> peek timePtr+ peek timePtr x509CertGetPubKeyFingerprint@@ -283,12 +282,13 @@ -- a negative error or INVALID_IDENTIFIER to return a bool. -- -- Note: unexplained function, best-guess implementation-x509CertAllowedUsage- :: X509Cert -- ^ __cert__- -> X509KeyConstraints -- ^ __key_usage__- -> IO Bool-x509CertAllowedUsage cert usage = withX509Cert cert $ \ certPtr -> do- throwBotanCatchingSuccess $ botan_x509_cert_allowed_usage certPtr usage+x509CertAllowedUsage ::+ X509Cert -- ^ __cert__+ -> X509KeyConstraints -- ^ __key_usage__+ -> IO Bool+x509CertAllowedUsage cert usage =+ withX509Cert cert $ \ certPtr ->+ throwBotanCatchingInvalidVerifier $ botan_x509_cert_allowed_usage certPtr usage {- | Check if the certificate matches the specified hostname via alternative name or CN match.@@ -296,15 +296,16 @@ Note: unexplained function, best-guess implementation -}-x509CertHostnameMatch- :: X509Cert -- ^ __cert__- -> ByteString -- ^ __hostname__- -> IO Bool-x509CertHostnameMatch cert hostname = withX509Cert cert $ \ certPtr -> do- asCString hostname $ \ hostnamePtr -> do- throwBotanCatchingSuccess $ botan_x509_cert_hostname_match- certPtr- (ConstPtr hostnamePtr)+x509CertHostnameMatch ::+ X509Cert -- ^ __cert__+ -> ByteString -- ^ __hostname__+ -> IO Bool+x509CertHostnameMatch cert hostname =+ withX509Cert cert $ \ certPtr ->+ asCString hostname $ \ hostnamePtr ->+ throwBotanCatchingInvalidInput $ botan_x509_cert_hostname_match+ certPtr+ (ConstPtr hostnamePtr) {- | Returns 0 if the validation was successful, 1 if validation failed,@@ -314,35 +315,35 @@ Intermediates or trusted lists can be null Trusted path can be null -}-x509CertVerify- :: X509Cert -- ^ __cert__- -> [X509Cert] -- ^ __intermediates__- -> [X509Cert] -- ^ __trusted__- -> Maybe FilePath -- ^ __trusted_path__- -> Int -- ^ __required_strength__- -> ByteString -- ^ __hostname__- -> Word64 -- ^ __reference_time__- -> IO (Bool, Int) -- ^ __(valid,validation_result)__-x509CertVerify cert icerts tcerts tpath strength hostname time = do- withX509Cert cert $ \ certPtr -> do- withPtrs withX509Cert icerts $ flip withArrayLen $ \ icertsLen icertsPtr -> do- withPtrs withX509Cert tcerts $ flip withArrayLen $ \ tcertsLen tcertsPtr -> do- maybe ($ nullPtr) String.withCString tpath $ \ tpathPtr -> do- asCString hostname $ \ hostnamePtr -> do- alloca $ \ statusPtr -> do- success <- throwBotanCatchingSuccess $ botan_x509_cert_verify- statusPtr- certPtr- (ConstPtr icertsPtr)- (fromIntegral icertsLen)- (ConstPtr tcertsPtr)- (fromIntegral tcertsLen)- (ConstPtr tpathPtr)- (fromIntegral strength)- (ConstPtr hostnamePtr)- time- code <- fromIntegral <$> peek statusPtr- return (success, code)+x509CertVerify ::+ X509Cert -- ^ __cert__+ -> [X509Cert] -- ^ __intermediates__+ -> [X509Cert] -- ^ __trusted__+ -> Maybe FilePath -- ^ __trusted_path__+ -> Int -- ^ __required_strength__+ -> ByteString -- ^ __hostname__+ -> Word64 -- ^ __reference_time__+ -> IO (Bool, Int) -- ^ __(valid,validation_result)__+x509CertVerify cert icerts tcerts tpath strength hostname time =+ withX509Cert cert $ \ certPtr ->+ withPtrs withX509Cert icerts $ flip withArrayLen $ \ icertsLen icertsPtr ->+ withPtrs withX509Cert tcerts $ flip withArrayLen $ \ tcertsLen tcertsPtr ->+ maybe ($ nullPtr) String.withCString tpath $ \ tpathPtr ->+ asCString hostname $ \ hostnamePtr ->+ alloca $ \ statusPtr -> do+ success <- throwBotanCatchingInvalidVerifier $ botan_x509_cert_verify+ statusPtr+ certPtr+ (ConstPtr icertsPtr)+ (fromIntegral icertsLen)+ (ConstPtr tcertsPtr)+ (fromIntegral tcertsLen)+ (ConstPtr tpathPtr)+ (fromIntegral strength)+ (ConstPtr hostnamePtr)+ time+ code <- fromIntegral <$> peek statusPtr+ return (success, code) -- TODO: The above works, but there's more to it -- Need to allow null pointer for empty lists too, something like: -- where@@ -356,7 +357,7 @@ status <- botan_x509_cert_validation_status (fromIntegral code) if status == ConstPtr nullPtr then return Nothing- else Just <$> packCString (unConstPtr status)+ else Just <$> packCString status.unConstPtr -- /* -- * X.509 CRL@@ -366,12 +367,11 @@ newtype X509CRL = MkX509CRL { getX509CRLForeignPtr :: ForeignPtr BotanX509CRLStruct } -newX509CRL :: BotanX509CRL -> IO X509CRL withX509CRL :: X509CRL -> (BotanX509CRL -> IO a) -> IO a x509CRLDestroy :: X509CRL -> IO () createX509CRL :: (Ptr BotanX509CRL -> IO CInt) -> IO X509CRL-(newX509CRL, withX509CRL, x509CRLDestroy, createX509CRL, _)- = mkBindings MkBotanX509CRL runBotanX509CRL MkX509CRL getX509CRLForeignPtr botan_x509_crl_destroy+(withX509CRL, x509CRLDestroy, createX509CRL)+ = mkBindings MkBotanX509CRL (.runBotanX509CRL) MkX509CRL (.getX509CRLForeignPtr) botan_x509_crl_destroy x509CRLLoad :: ByteString -- ^ __crl_bits[]__@@ -387,48 +387,49 @@ Given a CRL and a certificate, check if the certificate is revoked on that particular CRL -}-x509IsRevoked- :: X509CRL -- ^ __crl__- -> X509Cert -- ^ __cert__- -> IO Bool-x509IsRevoked crl cert = withX509CRL crl $ \ crlPtr -> do- withX509Cert cert $ \ certPtr -> do- throwBotanCatchingSuccess $ botan_x509_is_revoked crlPtr certPtr+x509IsRevoked ::+ X509CRL -- ^ __crl__+ -> X509Cert -- ^ __cert__+ -> IO Bool+x509IsRevoked crl cert =+ withX509CRL crl $ \ crlPtr ->+ withX509Cert cert $ \ certPtr ->+ throwBotanCatchingInvalidInput $ botan_x509_is_revoked crlPtr certPtr {- | Different flavor of `botan_x509_cert_verify`, supports revocation lists. CRLs are passed as an array, same as intermediates and trusted CAs -}-x509CertVerifyWithCLR- :: X509Cert -- ^ __cert__- -> [X509Cert] -- ^ __intermediates__- -> [X509Cert] -- ^ __trusted__- -> [X509CRL] -- ^ __crls__- -> Maybe FilePath -- ^ __trusted_path__- -> Int -- ^ __required_strength__- -> ByteString -- ^ __hostname__- -> Word64 -- ^ __reference_time__- -> IO (Bool, Int) -- ^ __(valid,validation_result)__-x509CertVerifyWithCLR cert icerts tcerts crls tpath strength hostname time = do- withX509Cert cert $ \ certPtr -> do- withPtrs withX509Cert icerts $ flip withArrayLen $ \ icertsLen icertsPtr -> do- withPtrs withX509Cert tcerts $ flip withArrayLen $ \ tcertsLen tcertsPtr -> do- withPtrs withX509CRL crls $ flip withArrayLen $ \ crlsLen crlsPtr -> do- maybe ($ nullPtr) String.withCString tpath $ \ tpathPtr -> do- asCString hostname $ \ hostnamePtr -> do- alloca $ \ statusPtr -> do- success <- throwBotanCatchingSuccess $ botan_x509_cert_verify_with_crl- statusPtr- certPtr- (ConstPtr icertsPtr)- (fromIntegral icertsLen)- (ConstPtr tcertsPtr)- (fromIntegral tcertsLen)- (ConstPtr crlsPtr)- (fromIntegral crlsLen)- (ConstPtr tpathPtr)- (fromIntegral strength)- (ConstPtr hostnamePtr)- time- code <- fromIntegral <$> peek statusPtr- return (success, code)+x509CertVerifyWithCLR ::+ X509Cert -- ^ __cert__+ -> [X509Cert] -- ^ __intermediates__+ -> [X509Cert] -- ^ __trusted__+ -> [X509CRL] -- ^ __crls__+ -> Maybe FilePath -- ^ __trusted_path__+ -> Int -- ^ __required_strength__+ -> ByteString -- ^ __hostname__+ -> Word64 -- ^ __reference_time__+ -> IO (Bool, Int) -- ^ __(valid,validation_result)__+x509CertVerifyWithCLR cert icerts tcerts crls tpath strength hostname time =+ withX509Cert cert $ \ certPtr ->+ withPtrs withX509Cert icerts $ flip withArrayLen $ \ icertsLen icertsPtr ->+ withPtrs withX509Cert tcerts $ flip withArrayLen $ \ tcertsLen tcertsPtr ->+ withPtrs withX509CRL crls $ flip withArrayLen $ \ crlsLen crlsPtr ->+ maybe ($ nullPtr) String.withCString tpath $ \ tpathPtr ->+ asCString hostname $ \ hostnamePtr ->+ alloca $ \ statusPtr -> do+ success <- throwBotanCatchingInvalidVerifier $ botan_x509_cert_verify_with_crl+ statusPtr+ certPtr+ (ConstPtr icertsPtr)+ (fromIntegral icertsLen)+ (ConstPtr tcertsPtr)+ (fromIntegral tcertsLen)+ (ConstPtr crlsPtr)+ (fromIntegral crlsLen)+ (ConstPtr tpathPtr)+ (fromIntegral strength)+ (ConstPtr hostnamePtr)+ time+ code <- fromIntegral <$> peek statusPtr+ return (success, code)
− src/Botan/Low/X509/CA.hs
@@ -1,115 +0,0 @@-module Botan.Low.X509.CA where--import Data.Bool--import Botan.Low.Error-import Botan.Low.Prelude-import Botan.Low.Hash-import Botan.Low.Make-import Botan.Low.MPI-import Botan.Low.PubKey-import Botan.Low.PubKey.Sign-import Botan.Low.RNG-import Botan.Low.X509-import Botan.Low.X509.CSR-import Botan.Low.X509.Extensions--import Botan.Bindings.X509-import Botan.Bindings.X509.CA-import Botan.Bindings.X509.Extensions---- /*--- * X.509 certificate authority--- **************************/--newtype X509CA = MkX509CA { getX509CAForeignPtr :: ForeignPtr X509CAStruct }--withX509CAPtr :: X509CA -> (X509CAPtr -> IO a) -> IO a-withX509CAPtr = withForeignPtr . getX509CAForeignPtr--x509CADestroy :: X509CA -> IO ()-x509CADestroy ca = finalizeForeignPtr (getX509CAForeignPtr ca)--x509CACreate :: X509Cert -> PrivKey -> HashName -> RNG -> IO X509CA-x509CACreate cert key hash_fn rng = do- withX509Cert cert $ \ certPtr -> do- withPrivKey key $ \ keyPtr -> do- asCString hash_fn $ \ hashPtr -> do- withRNG rng $ \ botanRNG -> do- mkInit- MkX509CA- (\ caPtr -> botan_x509_ca_create- caPtr- certPtr- keyPtr- hashPtr- botanRNG- )- botan_x509_ca_destroy--x509CACreatePadding :: X509Cert -> PrivKey -> HashName -> X509PaddingName -> RNG -> IO X509CA-x509CACreatePadding cert key hash_fn padding_fn rng = do- withX509Cert cert $ \ certPtr -> do- withPrivKey key $ \ keyPtr -> do- asCString hash_fn $ \ hashPtr -> do- asCString padding_fn $ \ paddingPtr -> do- withRNG rng $ \ botanRNG -> do- mkInit- MkX509CA- (\ caPtr -> botan_x509_ca_create_padding- caPtr- certPtr- keyPtr- hashPtr- paddingPtr- botanRNG- )- botan_x509_ca_destroy--x509CASignRequest :: X509CA -> X509CSR -> RNG -> Word64 -> Word64 -> IO X509Cert-x509CASignRequest ca csr rng not_before not_after = do- withX509CAPtr ca $ \ caPtr -> do- withX509CSRPtr csr $ \ csrPtr -> do- withRNG rng $ \ botanRNG -> do- mkInit- MkX509Cert- (\ certPtr -> botan_x509_ca_sign_request certPtr caPtr csrPtr botanRNG not_before not_after)- botan_x509_cert_destroy---- NOTE: This is a static function on X509CA, so it doesn't take an actual X509CA object-x509CAMakeCertSerial :: SignCtx -> RNG -> MP -> SignAlgoName -> PubKey -> Word64 -> Word64 -> X509SubjectDN -> X509IssuerDN -> X509Extensions -> IO X509Cert-x509CAMakeCertSerial signer rng serial signalgo pubkey not_before not_after subject_dn issuer_dn exts = do- withSignPtr signer $ \ signerPtr -> do- withRNG rng $ \ botanRNG -> do- withMP serial $ \ serialPtr -> do- asCString signalgo $ \ signalgoPtr -> do- withPubKey pubkey $ \ pubkeyPtr -> do- asBytesLen subject_dn $ \ subject_dn_ptr subject_dn_len -> do- asBytesLen issuer_dn $ \ issuer_dn_ptr issuer_dn_len -> do- withX509ExtensionsPtr exts $ \ extsPtr -> do- mkInit- MkX509Cert- (\ certPtr -> botan_x509_ca_make_cert_serial- certPtr- signerPtr- botanRNG- serialPtr- signalgoPtr- pubkeyPtr- not_before- not_after- subject_dn_ptr subject_dn_len- issuer_dn_ptr issuer_dn_len- extsPtr- )- botan_x509_cert_destroy--x509CAChooseExtensions :: X509CSR -> X509Cert -> HashName -> IO X509Extensions-x509CAChooseExtensions csr cert hash_fn = do- withX509CSRPtr csr $ \ csrPtr -> do- withX509Cert cert $ \ certPtr -> do- asCString hash_fn $ \ hashPtr -> do- mkInit- MkX509Extensions- (\ extsPtr -> botan_x509_ca_choose_extensions extsPtr csrPtr certPtr hashPtr)- botan_x509_exts_destroy
− src/Botan/Low/X509/CRL.hs
@@ -1,151 +0,0 @@-module Botan.Low.X509.CRL where--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.X509-import Botan.Low.X509.Extensions--import Botan.Bindings.X509-import Botan.Bindings.X509.Extensions-import Botan.Bindings.X509.CRL-import Botan.Low.X509.CSR (X509IssuerDN)------- CRL------- TODO: Eventually move original / read-only CRL functions here too--x509CRLGetRevoked :: X509CRL -> IO [X509CRLEntry]-x509CRLGetRevoked crl = withX509CRL crl $ \ crlPtr -> do- -- TODO: Some sort of allocArrayQuerying- undefined--x509CRLGetIssuerDN :: X509CRL -> ByteString -> Int -> IO X509IssuerDN-x509CRLGetIssuerDN crl key index = withX509CRL crl $ \ crlPtr -> do- asBytes key $ \ keyPtr -> do- allocBytesQuerying $ \ outPtr outLen -> botan_x509_crl_get_issuer_dn- outPtr- outLen- crlPtr- keyPtr- (fromIntegral index)--x509CRLExtensions :: X509CRL -> IO X509Extensions-x509CRLExtensions crl = withX509CRL crl $ \ crlPtr -> mkInit- MkX509Extensions- (\ ptr -> botan_x509_crl_extensions ptr crlPtr)- botan_x509_exts_destroy--x509CRLAuthorityKeyId :: X509CRL -> IO ByteString-x509CRLAuthorityKeyId crl = withX509CRL crl $ \ crlPtr -> do- allocBytesQuerying $ \ outPtr outLen -> botan_x509_crl_authority_key_id- outPtr outLen- crlPtr--x509CRLSerialNumber :: X509CRL -> IO Word32-x509CRLSerialNumber crl = withX509CRL crl $ \ crlPtr -> do- alloca $ \ snPtr -> do- throwBotanIfNegative_ $ botan_x509_crl_serial_number snPtr crlPtr- peek snPtr--x509CRLThisUpdate :: X509CRL -> IO Word64-x509CRLThisUpdate crl = withX509CRL crl $ \ crlPtr -> do- alloca $ \ timePtr -> do- throwBotanIfNegative_ $ botan_x509_crl_this_update timePtr crlPtr- peek timePtr--x509CRLNextUpdate :: X509CRL -> IO Word64-x509CRLNextUpdate crl = withX509CRL crl $ \ crlPtr -> do- alloca $ \ timePtr -> do- throwBotanIfNegative_ $ botan_x509_crl_next_update timePtr crlPtr- peek timePtr--x509CRLIssuingDistributionPoint :: X509CRL -> IO ByteString-x509CRLIssuingDistributionPoint crl = withX509CRL crl $ \ crlPtr -> do- allocBytesQuerying $ \ outPtr outLen -> botan_x509_crl_issuing_distribution_point- outPtr outLen- crlPtr--x509CRLCreateDER :: ByteString -> IO X509CRL-x509CRLCreateDER der = do- asBytesLen der $ \ derPtr derLen -> mkInit- MkX509CRL- (\ptr -> botan_x509_crl_create_der ptr derPtr derLen)- botan_x509_crl_destroy--x509CRLCreate :: X509IssuerDN -> Word64 -> Word64 -> [X509CRLEntry] -> IO X509CRL-x509CRLCreate der this next entries = do- -- TODO: withArray, ownership transfer- -- asBytesLen der $ \ derPtr derLen -> mkInit- -- MkX509CRL- -- (\ptr -> botan_x509_crl_create ptr derPtr derLen)- -- botan_x509_crl_destroy- undefined--x509CRLAddEntry :: X509CRL -> X509CRLEntry -> IO ()-x509CRLAddEntry crl entry = undefined--x509CRLRevokeCert :: X509CRL -> X509Cert -> X509CRLCode -> IO ()-x509CRLRevokeCert crl entry reason = undefined------- CRL Entry-----newtype X509CRLEntry = MkX509CRLEntry { getX509CRLEntryForeignPtr :: ForeignPtr X509CRLEntryStruct }--withX509CRLEntryPtr :: X509CRLEntry -> (X509CRLEntryPtr -> IO a) -> IO a-withX509CRLEntryPtr = withForeignPtr . getX509CRLEntryForeignPtr--x509CRLEntryDestroy :: X509CRLEntry -> IO ()-x509CRLEntryDestroy crl = finalizeForeignPtr (getX509CRLEntryForeignPtr crl)--type X509CRLCode = Word32---- Must match values of CRL_Code in pkix_enums.h-pattern BOTAN_X509_CRL_CODE_UNSPECIFIED = 0 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_KEY_COMPROMISE = 1 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_CA_COMPROMISE = 2 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_AFFILIATION_CHANGED = 3 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_SUPERCEDED = 4 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_CESSATION_OF_OPERATION = 5 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_CERTIFICATE_HOLD = 6 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_REMOVE_FROM_CRL = 8 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_PRIVILEGE_WITHDRAWN = 9 :: X509CRLCode-pattern BOTAN_X509_CRL_CODE_AA_COMPROMISE = 10 :: X509CRLCode--x509CRLEntryCreate :: X509Cert -> X509CRLCode -> IO X509CRLEntry-x509CRLEntryCreate cert crl_code = do- withX509Cert cert $ \ certPtr -> mkInit- MkX509CRLEntry- (\ptr -> botan_x509_crl_entry_create ptr certPtr crl_code)- botan_x509_crl_entry_destroy---- -- TODO: CRL_Entry PEM / BER encode / decode (and for CRL too)---- NOTE: Is bytestring serial number vs MP serial number elsewhere - need to consistentize-x509CRLEntryGetSerialNumber :: X509CRLEntry -> IO ByteString-x509CRLEntryGetSerialNumber entry = withX509CRLEntryPtr entry $ \ entryPtr -> do- allocBytesQuerying $ \ outPtr szPtr -> do- botan_x509_crl_entry_get_serial_number outPtr szPtr entryPtr--x509CRLEntryGetExpireTime :: X509CRLEntry -> IO Word64-x509CRLEntryGetExpireTime entry = withX509CRLEntryPtr entry $ \ entryPtr -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_crl_entry_get_expire_time outPtr entryPtr- peek outPtr--x509CRLEntryGetReasonCode :: X509CRLEntry -> IO X509CRLCode-x509CRLEntryGetReasonCode entry = withX509CRLEntryPtr entry $ \ entryPtr -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_crl_entry_get_reason_code outPtr entryPtr- peek outPtr--x509CRLEntryGetExtensions :: X509CRLEntry -> IO X509Extensions-x509CRLEntryGetExtensions entry = withX509CRLEntryPtr entry $ \ entryPtr -> do- mkInit- MkX509Extensions- (\ ptr -> botan_x509_crl_entry_get_extensions ptr entryPtr)- botan_x509_exts_destroy
− src/Botan/Low/X509/CSR.hs
@@ -1,85 +0,0 @@-module Botan.Low.X509.CSR where--import Botan.Low.Error-import Botan.Low.Hash (HashName(..))-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.RNG-import Botan.Low.X509-import Botan.Low.X509.Extensions-import Botan.Low.X509.Options--import Botan.Bindings.X509-import Botan.Bindings.X509.CSR---- /*--- * X.509 certificate signing request--- **************************/--type X509SubjectDN = ByteString-type X509IssuerDN = ByteString---- NOTE: It is unknown which paddings are accepted (eg, compare cbc vs pk padding)-type X509PaddingName = ByteString-type X509Challenge = ByteString--newtype X509CSR = MkX509CSR { getX509CSRForeignPtr :: ForeignPtr X509CSRStruct }--withX509CSRPtr :: X509CSR -> (X509CSRPtr -> IO a) -> IO a-withX509CSRPtr = withForeignPtr . getX509CSRForeignPtr--x509CSRDestroy :: X509CSR -> IO ()-x509CSRDestroy csr = finalizeForeignPtr (getX509CSRForeignPtr csr)---- NOTE: It really appears that this is the successful init pattern;--- I ought to formalize it using type-level lists or something-x509CreateCertReq :: X509CertOptions -> PrivKey -> HashName -> RNG -> IO X509CSR-x509CreateCertReq options key hash_fn rng = withX509CertOptionsPtr options $ \ optionsPtr -> do- withPrivKey key $ \ keyPtr -> do- asCString hash_fn $ \ hashPtr -> do- withRNG rng $ \ botanRNG -> do- mkInit- MkX509CSR- (\ csrPtr -> botan_x509_create_cert_req csrPtr optionsPtr keyPtr hashPtr botanRNG)- botan_x509_csr_destroy--x509CSRCreate :: PrivKey -> X509SubjectDN -> X509Extensions -> HashName -> RNG -> X509PaddingName -> X509Challenge -> IO X509CSR-x509CSRCreate privkey subjectDN exts hash_fn rng padding_fn challenge = do- withPrivKey privkey $ \ privkeyPtr -> do- asBytesLen subjectDN $ \ subjectDNPtr subjectDNLen -> do- withX509ExtensionsPtr exts $ \ extsPtr -> do- asCString hash_fn $ \ hashPtr -> do- withRNG rng $ \ botanRNG -> do- asCString padding_fn $ \ paddingPtr -> do- asCString challenge $ \ challengePtr -> do- mkInit- MkX509CSR- (\ csrPtr -> botan_x509_csr_create- csrPtr- privkeyPtr- subjectDNPtr subjectDNLen- extsPtr- hashPtr- botanRNG- paddingPtr- challengePtr- )- botan_x509_csr_destroy--x509CreateSelfSignedCert :: X509CertOptions -> PrivKey -> HashName -> RNG -> IO X509Cert-x509CreateSelfSignedCert options key hash_fn rng = do- withX509CertOptionsPtr options $ \ optionsPtr -> do- withPrivKey key $ \ keyPtr -> do- asCString hash_fn $ \ hashPtr -> do- withRNG rng $ \ botanRNG -> do- mkInit- MkX509Cert- (\ certPtr -> botan_x509_create_self_signed_cert- certPtr- optionsPtr- keyPtr- hashPtr- botanRNG- )- botan_x509_cert_destroy
− src/Botan/Low/X509/DN.hs
@@ -1,60 +0,0 @@-module Botan.Low.X509.DN where--import Botan.Low.Error-import Botan.Low.Prelude-import Botan.Low.X509--import Botan.Bindings.X509.DN--newtype X509DN = MkX509DN { getX509DNForeignPtr :: ForeignPtr X509DNStruct }--withX509DNPtr :: X509DN -> (X509DNPtr -> IO a) -> IO a-withX509DNPtr = withForeignPtr . getX509DNForeignPtr--x509DNDestroy :: X509DN -> IO ()-x509DNDestroy dn = finalizeForeignPtr (getX509DNForeignPtr dn)---- foreign import ccall unsafe botan_x509_dn_create--- :: Ptr X509DNPtr--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_create_from_multimap--- :: Ptr X509DNPtr--- -> Ptr Word8 -> Ptr CSize--- -> Ptr Word8 -> Ptr CSize--- -> CSize--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_to_string--- :: Ptr Word8 -> Ptr CSize--- -> X509DNPtr--- -> IO BotanErrorCode---- -- NOTE: Returns a bool success code--- foreign import ccall unsafe botan_x509_dn_has_field--- :: X509DNPtr--- -> Ptr Word8 -> CSize--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_get_first_attribute--- :: Ptr Word8 -> Ptr CSize--- -> X509DNPtr--- -> Ptr Word8 -> CSize--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_get_attribute--- :: Ptr (Ptr Word8) -> Ptr CSize -> Ptr CSize--- -> X509DNPtr--- -> Ptr Word8 -> CSize--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_contents--- :: Ptr (Ptr Word8) -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> Ptr CSize--- -> X509DNPtr--- -> IO BotanErrorCode---- foreign import ccall unsafe botan_x509_dn_add_attribute--- :: X509DNPtr--- -> Ptr Word8 -> CSize--- -> Ptr Word8 -> CSize--- -> IO BotanErrorCode
− src/Botan/Low/X509/Extensions.hs
@@ -1,26 +0,0 @@-module Botan.Low.X509.Extensions where--import Botan.Low.Error-import Botan.Low.Prelude-import Botan.Low.X509--import Botan.Bindings.X509.Extensions--newtype X509Extensions = MkX509Extensions { getX509ExtensionsForeignPtr :: ForeignPtr X509ExtensionsStruct }--withX509ExtensionsPtr :: X509Extensions -> (X509ExtensionsPtr -> IO a) -> IO a-withX509ExtensionsPtr = withForeignPtr . getX509ExtensionsForeignPtr--x509ExtensionsDestroy :: X509Extensions -> IO ()-x509ExtensionsDestroy exts = finalizeForeignPtr (getX509ExtensionsForeignPtr exts)--newtype X509Extension = MkX509Extension { getX509ExtensionForeignPtr :: ForeignPtr X509ExtensionStruct }--withX509ExtensionPtr :: X509Extension -> (X509ExtensionPtr -> IO a) -> IO a-withX509ExtensionPtr = withForeignPtr . getX509ExtensionForeignPtr--x509ExtensionDestroy :: X509Extension -> IO ()-x509ExtensionDestroy ext = finalizeForeignPtr (getX509ExtensionForeignPtr ext)---- TODO: See C FFI for discussion on pending implementation-
− src/Botan/Low/X509/Options.hs
@@ -1,157 +0,0 @@-module Botan.Low.X509.Options where--import Data.Bool--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.X509-import Botan.Low.X509.Extensions--import Botan.Bindings.X509.Options---- /*--- * X.509 certificate options--- **************************/--newtype X509CertOptions = MkX509CertOptions { getX509CertOptionsForeignPtr :: ForeignPtr X509CertOptionsStruct }--withX509CertOptionsPtr :: X509CertOptions -> (X509CertOptionsPtr -> IO a) -> IO a-withX509CertOptionsPtr = withForeignPtr . getX509CertOptionsForeignPtr--x509CertOptionsDestroy :: X509CertOptions -> IO ()-x509CertOptionsDestroy opts = finalizeForeignPtr (getX509CertOptionsForeignPtr opts)--x509CertOptionsCreate :: IO X509CertOptions-x509CertOptionsCreate = mkInit MkX509CertOptions botan_x509_cert_options_create botan_x509_cert_options_destroy--x509CertOptionsCreateCommon- :: ByteString- -> ByteString- -> ByteString- -> ByteString- -> Word32- -> IO X509CertOptions-x509CertOptionsCreateCommon common_name country org org_unit expiration_time = do- alloca $ \ outPtr -> do- asCString common_name $ \ cnPtr -> do- asCString country $ \ countryPtr -> do- asCString org $ \ orgPtr -> do- asCString org_unit $ \ orgUnitPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_options_create_common outPtr- cnPtr- countryPtr- orgPtr- orgUnitPtr- expiration_time- out <- peek outPtr- foreignPtr <- newForeignPtr botan_x509_cert_options_destroy out- return $ MkX509CertOptions foreignPtr--x509CertOptionsSetCommonName :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetCommonName = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_common_name--x509CertOptionsSetCountry :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetCountry = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_country--x509CertOptionsSetOrg :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetOrg = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_organization--x509CertOptionsSetOrgUnit :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetOrgUnit = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_org_unit--x509CertOptionsSetMoreOrgUnits :: X509CertOptions -> [ByteString] -> IO ()-x509CertOptionsSetMoreOrgUnits opts more_org_units = withX509CertOptionsPtr opts $ \ optsPtr -> do- withPtrs asCString more_org_units $ \ morePtrs -> do- allocaArray moreLen $ \ (morePtrArrayPtr :: Ptr (Ptr CChar)) -> do- pokeArray morePtrArrayPtr morePtrs- throwBotanIfNegative_ $ botan_x509_cert_options_set_more_org_units- optsPtr- morePtrArrayPtr- (fromIntegral moreLen)- where- moreLen = length more_org_units--x509CertOptionsSetLocality :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetLocality = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_locality--x509CertOptionsSetState :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetState = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_state--x509CertOptionsSetSerialNumber :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetSerialNumber = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_serial_number--x509CertOptionsSetEmail :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetEmail = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_email--x509CertOptionsSetURI :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetURI = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_uri--x509CertOptionsSetIP :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetIP = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_ip--x509CertOptionsSetDNS :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetDNS = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_dns--x509CertOptionsSetMoreDNS :: X509CertOptions -> [ByteString] -> IO ()-x509CertOptionsSetMoreDNS opts more_dns = withX509CertOptionsPtr opts $ \ optsPtr -> do- withPtrs asCString more_dns $ \ morePtrs -> do- allocaArray moreLen $ \ (morePtrArrayPtr :: Ptr (Ptr CChar)) -> do- pokeArray morePtrArrayPtr morePtrs- throwBotanIfNegative_ $ botan_x509_cert_options_set_more_dns- optsPtr- morePtrArrayPtr- (fromIntegral moreLen)- where- moreLen = length more_dns--x509CertOptionsSetXMPP :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetXMPP = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_xmpp--x509CertOptionsSetChallenge :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetChallenge = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_challenge--x509CertOptionsSetStart :: X509CertOptions -> Word64 -> IO ()-x509CertOptionsSetStart = mkSet withX509CertOptionsPtr botan_x509_cert_options_set_start--x509CertOptionsSetEnd :: X509CertOptions -> Word64 -> IO ()-x509CertOptionsSetEnd = mkSet withX509CertOptionsPtr botan_x509_cert_options_set_end---- // TODO: Convenience functions for set_start_duration, set_expires--x509CertOptionsSetIsCA :: X509CertOptions -> Bool -> IO ()-x509CertOptionsSetIsCA = mkSetOn withX509CertOptionsPtr (CBool . bool 0 1) botan_x509_cert_options_set_is_ca--x509CertOptionsSetPathLimit :: X509CertOptions -> Int -> IO ()-x509CertOptionsSetPathLimit = mkSetCSize withX509CertOptionsPtr botan_x509_cert_options_set_path_limit--x509CertOptionsSetPaddingScheme :: X509CertOptions -> ByteString -> IO ()-x509CertOptionsSetPaddingScheme = mkSetCString withX509CertOptionsPtr botan_x509_cert_options_set_padding_scheme---- NOTE: Should probably be Word rather than Int-x509CertOptionsSetKeyConstraints :: X509CertOptions -> Int -> IO ()-x509CertOptionsSetKeyConstraints = mkSetOn withX509CertOptionsPtr fromIntegral botan_x509_cert_options_set_key_constraints--x509CertOptionsSetExConstraints :: X509CertOptions -> [ByteString] -> IO ()-x509CertOptionsSetExConstraints opts exConstraints = withX509CertOptionsPtr opts $ \ optsPtr -> do- withPtrs asCString exConstraints $ \ exPtrs -> do- allocaArray exLen $ \ (exPtrArrayPtr :: Ptr (Ptr CChar)) -> do- pokeArray exPtrArrayPtr exPtrs- throwBotanIfNegative_ $ botan_x509_cert_options_set_ex_constraints- optsPtr- exPtrArrayPtr- (fromIntegral exLen)- where- exLen = length exConstraints--x509CertOptionsSetExtensions :: X509CertOptions -> X509Extensions -> IO ()-x509CertOptionsSetExtensions opts extensions = withX509CertOptionsPtr opts $ \ optsPtr -> do- withX509ExtensionsPtr extensions $ \ extensionPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_options_set_extensions- optsPtr- extensionPtr---- TODO: botan_x509_cert_options getters---- TODO: botan_x509_cert_options functions (above is just members)--- There's only a few though so its easy
− src/Botan/Low/X509/Path.hs
@@ -1,141 +0,0 @@-module Botan.Low.X509.Path where--import Foreign.Marshal.Utils--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.X509-import Botan.Low.X509.Store--import Botan.Bindings.X509-import Botan.Bindings.X509.Path--newtype X509PathRestrictions = MkX509PathRestrictions { getX509PathRestrictionsForeignPtr :: ForeignPtr X509PathRestrictionsStruct }--withX509PathRestrictionsPtr :: X509PathRestrictions -> (X509PathRestrictionsPtr -> IO a) -> IO a-withX509PathRestrictionsPtr = withForeignPtr . getX509PathRestrictionsForeignPtr--x509PathRestrictionsDestroy :: X509PathRestrictions -> IO ()-x509PathRestrictionsDestroy restrictions = finalizeForeignPtr (getX509PathRestrictionsForeignPtr restrictions)--x509PathRestrictionsCreate :: Bool -> Int -> Bool -> Word64 -> X509CertStore -> IO X509PathRestrictions-x509PathRestrictionsCreate require_rev minimum_key_strength ocsp_all_intermediates max_ocsp_age trusted_ocsp_responders = do- withX509CertStorePtr trusted_ocsp_responders $ \ trustedStorePtr -> do- mkInit- MkX509PathRestrictions- (\ pvPtr -> botan_x509_path_restrictions_create- pvPtr- (fromBool require_rev)- (fromIntegral minimum_key_strength)- (fromBool ocsp_all_intermediates)- max_ocsp_age- trustedStorePtr- )- botan_x509_path_restrictions_destroy---- TODO: x509PathRestrictionsCreateTrustedHashes--newtype X509PathValidation = MkX509PathValidation { getX509PathValidationForeignPtr :: ForeignPtr X509PathValidationStruct }--withX509PathValidationPtr :: X509PathValidation -> (X509PathValidationPtr -> IO a) -> IO a-withX509PathValidationPtr = withForeignPtr . getX509PathValidationForeignPtr--x509PathValidationDestroy :: X509PathValidation -> IO ()-x509PathValidationDestroy validation = finalizeForeignPtr (getX509PathValidationForeignPtr validation)---- NOTE: OCSPResponse not yet implemented-x509PathValidate :: X509Cert -> X509PathRestrictions -> X509CertStore -> ByteString -> Word32 -> Word64 -> Word64 -> Ptr () -> IO X509PathValidation-x509PathValidate cert restrictions store hostname usage validationTime ocspTimeout ocspResponse = do- withX509Cert cert $ \ certPtr -> do- withX509PathRestrictionsPtr restrictions $ \ restrictionsPtr -> do- withX509CertStorePtr store $ \ storePtr -> do- asCString hostname $ \ hostnamePtr -> do- mkInit- MkX509PathValidation- (\ pvPtr -> botan_x509_path_validate- pvPtr- certPtr- restrictionsPtr- storePtr- hostnamePtr- (fromIntegral usage)- validationTime- ocspTimeout- ocspResponse- )- botan_x509_path_validation_destroy--x509PathValidationSuccessfulValidation :: X509PathValidation -> IO Bool-x509PathValidationSuccessfulValidation pv = withX509PathValidationPtr pv $ \ pvPtr -> do- throwBotanCatchingSuccess $ botan_x509_path_validation_successful_validation pvPtr--x509PathValidationResultString :: X509PathValidation -> IO ByteString-x509PathValidationResultString pv = withX509PathValidationPtr pv $ \ pvPtr -> do- allocBytesQueryingCString $ \ resultPtr resultLen -> do- botan_x509_path_validation_result_string resultPtr resultLen pvPtr--x509PathValidationTrustRoot :: X509PathValidation -> IO X509Cert-x509PathValidationTrustRoot pv = withX509PathValidationPtr pv $ \ pvPtr -> do- mkInit- MkX509Cert- (\ certPtr -> botan_x509_path_validation_trust_root certPtr pvPtr)- botan_x509_cert_destroy---- TODO: Need some sort of allocArrayQuerying, or a mkArrayInit-x509PathValidationCertPath :: X509PathValidation -> IO [X509Cert]-x509PathValidationCertPath pv = withX509PathValidationPtr pv $ \ pvPtr -> do- let fn arrPtr szPtr = botan_x509_path_validation_cert_path arrPtr szPtr pvPtr- alloca $ \ szPtr -> do- code <- fn nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ fn arrPtr szPtr- certPtrs <- peekArray sz arrPtr- -- NOTE: Cannot use mkInit because that performs an alloca and a peek,- -- whereas we have already performed that work here- forM certPtrs $ \ certPtr -> do- foreignPtr <- newForeignPtr botan_x509_cert_destroy certPtr- return $ MkX509Cert foreignPtr- _ -> throwBotanError code--type X509PathValidationStatusCode = Int--x509PathValidationStatusCode :: X509PathValidation -> IO X509PathValidationStatusCode-x509PathValidationStatusCode pv = withX509PathValidationPtr pv $ \ pvPtr -> do- alloca $ \ statusCodePtr -> do- throwBotanIfNegative_ $ botan_x509_path_validation_status_code statusCodePtr pvPtr- fromIntegral <$> peek statusCodePtr---- TODO: Need some sort of allocArrayQuerying, or a mkArrayInit-x509PathValidationAllStatusCodes :: X509PathValidation -> IO [X509PathValidationStatusCode]-x509PathValidationAllStatusCodes pv = withX509PathValidationPtr pv $ \ pvPtr -> do- let fn arrPtr szPtr = botan_x509_path_validation_all_status_codes arrPtr szPtr pvPtr- alloca $ \ szPtr -> do- code <- fn nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ fn arrPtr szPtr- fmap fromIntegral <$> peekArray sz arrPtr- _ -> throwBotanError code---- NOTE: Array of strings - extra tricky, not finished yet, will be reused--- NOTE: Similar to x509DNGetAttribute / x509DNGetContents-x509PathValidationTrustedHashes :: X509PathValidation -> IO [ByteString]-x509PathValidationTrustedHashes pv = withX509PathValidationPtr pv $ \ pvPtr -> do- let fn arrPtr sizesPtr szPtr = botan_x509_path_validation_trusted_hashes arrPtr sizesPtr szPtr pvPtr- alloca $ \ szPtr -> do- code <- fn nullPtr nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ sizesPtr -> do- code' <- fn nullPtr sizesPtr szPtr- case code' of- -- TODO: Actually fill out array of strings- _ -> undefined- _ -> throwBotanError code
− src/Botan/Low/X509/Store.hs
@@ -1,326 +0,0 @@-module Botan.Low.X509.Store where--import Data.Bool-import Foreign.Marshal.Utils--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude-import Botan.Low.PubKey-import Botan.Low.RNG-import Botan.Low.X509--import Botan.Bindings.PubKey-import Botan.Bindings.X509-import Botan.Bindings.X509.Store--newtype X509CertStore = MkX509CertStore { getX509CertStoreForeignPtr :: ForeignPtr X509CertStoreStruct }--withX509CertStorePtr :: X509CertStore -> (X509CertStorePtr -> IO a) -> IO a-withX509CertStorePtr = withForeignPtr . getX509CertStoreForeignPtr--x509CertStoreDestroy :: X509CertStore -> IO ()-x509CertStoreDestroy ca = finalizeForeignPtr (getX509CertStoreForeignPtr ca)---- NOTE: mkInit cannot handle Maybes (checking for nullPtr), need a mkInitMaybe or to use maybePeek-x509CertStoreFindCert :: X509CertStore -> ByteString -> ByteString -> IO (Maybe X509Cert)-x509CertStoreFindCert store subject_dn key_id = withX509CertStorePtr store $ \ store_ptr -> do- asBytesLen subject_dn $ \ subject_dn_ptr subject_dn_len -> do- asBytesLen key_id $ \ key_id_ptr key_id_len -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_find_cert- outPtr- store_ptr- subject_dn_ptr- subject_dn_len- key_id_ptr- key_id_len- out <- peek outPtr- if out == nullPtr- then return Nothing- else do- foreignPtr <- newForeignPtr botan_x509_cert_destroy out- return $ Just $ MkX509Cert foreignPtr---- NOTE: Untested--- TODO: Need some sort of allocArrayQuerying, or a mkArrayInit-x509CertStoreFindAllCerts :: X509CertStore -> ByteString -> ByteString -> IO [X509Cert]-x509CertStoreFindAllCerts store subject_dn key_id = withX509CertStorePtr store $ \ store_ptr -> do- asBytesLen subject_dn $ \ subject_dn_ptr subject_dn_len -> do- asBytesLen key_id $ \ key_id_ptr key_id_len -> do- let fn arrPtr szPtr = botan_x509_cert_store_find_all_certs arrPtr szPtr store_ptr subject_dn_ptr subject_dn_len key_id_ptr key_id_len- alloca $ \ szPtr -> do- code <- fn nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ fn arrPtr szPtr- certPtrs <- peekArray sz arrPtr- -- NOTE: Cannot use mkInit because that performs an alloca and a peek,- -- whereas we have already performed that work here- forM certPtrs $ \ certPtr -> do- foreignPtr <- newForeignPtr botan_x509_cert_destroy certPtr- return $ MkX509Cert foreignPtr- _ -> throwBotanError code---- TODO: Use mkInitMaybe-x509CertStoreFindCertByPubkeySHA1 :: X509CertStore -> ByteString -> IO (Maybe X509Cert)-x509CertStoreFindCertByPubkeySHA1 store digest = withX509CertStorePtr store $ \ store_ptr -> do- asBytesLen digest $ \ digest_ptr _ -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_find_cert_by_pubkey_sha1- outPtr- store_ptr- digest_ptr- out <- peek outPtr- if out == nullPtr- then return Nothing- else do- foreignPtr <- newForeignPtr botan_x509_cert_destroy out- return $ Just $ MkX509Cert foreignPtr---- TODO: Use mkInitMaybe-x509CertStoreFindCertByRawSubjectDNSHA256 :: X509CertStore -> ByteString -> IO (Maybe X509Cert)-x509CertStoreFindCertByRawSubjectDNSHA256 store digest = withX509CertStorePtr store $ \ store_ptr -> do- asBytesLen digest $ \ digest_ptr _ -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_find_cert_by_raw_subject_dn_sha256- outPtr- store_ptr- digest_ptr- out <- peek outPtr- if out == nullPtr- then return Nothing- else do- foreignPtr <- newForeignPtr botan_x509_cert_destroy out- return $ Just $ MkX509Cert foreignPtr---- TODO: Use mkInitMaybe-x509CertStoreFindCRLFor :: X509CertStore -> X509Cert -> IO (Maybe X509CRL)-x509CertStoreFindCRLFor store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_find_crl_for- outPtr- store_ptr- cert_ptr- out <- peek outPtr- if out == nullPtr- then return Nothing- else do- foreignPtr <- newForeignPtr botan_x509_crl_destroy out- return $ Just $ MkX509CRL foreignPtr--x509CertStoreCertificateKnown :: X509CertStore -> X509Cert -> IO Bool-x509CertStoreCertificateKnown store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanCatchingSuccess $ botan_x509_cert_store_certificate_known store_ptr cert_ptr---- {---- In-memory cert store--- -}---- TODO: Get rid of bytestring paths-x509CertStoreInMemoryLoadDir :: ByteString -> IO X509CertStore-x509CertStoreInMemoryLoadDir path = asCString path $ \ path_ptr -> mkInit- MkX509CertStore- (\ ptr -> botan_x509_cert_store_in_memory_load_dir- ptr- path_ptr- )- botan_x509_cert_store_destroy--x509CertStoreInMemoryLoadCert :: X509Cert -> IO X509CertStore-x509CertStoreInMemoryLoadCert cert = withX509Cert cert $ \ cert_ptr -> mkInit- MkX509CertStore- (\ ptr -> botan_x509_cert_store_in_memory_load_cert- ptr- cert_ptr- )- botan_x509_cert_store_destroy--x509CertStoreInMemoryCreate :: IO X509CertStore-x509CertStoreInMemoryCreate = mkInit- MkX509CertStore- botan_x509_cert_store_in_memory_create- botan_x509_cert_store_destroy--x509CertStoreInMemoryAddCertificate :: X509CertStore -> X509Cert -> IO ()-x509CertStoreInMemoryAddCertificate store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_in_memory_add_certificate- store_ptr- cert_ptr--x509CertStoreInMemoryAddCRL :: X509CertStore -> X509CRL -> IO ()-x509CertStoreInMemoryAddCRL store crl = withX509CertStorePtr store $ \ store_ptr -> do- withX509CRL crl $ \ crl_ptr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_in_memory_add_crl- store_ptr- crl_ptr---- {---- Flatfile cert store--- -}--x509CertStoreFlatfileCreate :: ByteString -> Bool -> IO X509CertStore-x509CertStoreFlatfileCreate path ignore_non_ca = asCString path $ \ path_ptr -> mkInit- MkX509CertStore- (\ ptr -> botan_x509_cert_store_flatfile_create- ptr- path_ptr- (fromBool ignore_non_ca)- )- botan_x509_cert_store_destroy--{--SQL cert store--}--x509CertStoreSQLInsertCert :: X509CertStore -> X509Cert -> IO Bool-x509CertStoreSQLInsertCert store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanCatchingSuccess $ botan_x509_cert_store_sql_insert_cert- store_ptr- cert_ptr--x509CertStoreSQLRemoveCert :: X509CertStore -> X509Cert -> IO Bool-x509CertStoreSQLRemoveCert store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanCatchingSuccess $ botan_x509_cert_store_sql_remove_cert- store_ptr- cert_ptr---- TODO: Use mkInitMaybe-x509CertStoreSQLFindKey :: X509CertStore -> X509Cert -> IO (Maybe PrivKey)-x509CertStoreSQLFindKey store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- alloca $ \ outPtr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_sql_find_key- outPtr- store_ptr- cert_ptr- out <- peek outPtr- if out == nullPtr- then return Nothing- else do- foreignPtr <- newForeignPtr botan_privkey_destroy out- return $ Just $ MkPrivKey foreignPtr---- NOTE: See notes about returning arrays of things, improper return pointer type--- TODO: Need some sort of allocArrayQuerying, or a mkArrayInit-x509CertStoreSQLFindCertsForKey :: X509CertStore -> PrivKey -> IO [X509Cert]-x509CertStoreSQLFindCertsForKey store privkey = withX509CertStorePtr store $ \ store_ptr -> do- withPrivKey privkey $ \ privkey_ptr -> do- let fn arrPtr szPtr = botan_x509_cert_store_sql_find_certs_for_key arrPtr szPtr store_ptr privkey_ptr- alloca $ \ szPtr -> do- code <- fn nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ fn arrPtr szPtr- certPtrs <- peekArray sz arrPtr- -- NOTE: Cannot use mkInit because that performs an alloca and a peek,- -- whereas we have already performed that work here- forM certPtrs $ \ certPtr -> do- foreignPtr <- newForeignPtr botan_x509_cert_destroy certPtr- return $ MkX509Cert foreignPtr- _ -> throwBotanError code--x509CertStoreSQLInsertKey :: X509CertStore -> X509Cert -> PrivKey -> IO Bool-x509CertStoreSQLInsertKey store cert privkey = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- withPrivKey privkey $ \ privkey_ptr -> do- throwBotanCatchingSuccess $ botan_x509_cert_store_sql_insert_key- store_ptr- cert_ptr- privkey_ptr--x509CertStoreSQLRemoveKey :: X509CertStore -> PrivKey -> IO ()-x509CertStoreSQLRemoveKey store privkey = withX509CertStorePtr store $ \ store_ptr -> do- withPrivKey privkey $ \ privkey_ptr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_sql_remove_key- store_ptr- privkey_ptr--x509CertStoreSQLRevokeCert :: X509CertStore -> X509Cert -> Word32 -> Word64 -> IO ()-x509CertStoreSQLRevokeCert store cert crl_code time = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_sql_revoke_cert- store_ptr- cert_ptr- crl_code- time--x509CertStoreSQLAffirmCert :: X509CertStore -> X509Cert -> IO ()-x509CertStoreSQLAffirmCert store cert = withX509CertStorePtr store $ \ store_ptr -> do- withX509Cert cert $ \ cert_ptr -> do- throwBotanIfNegative_ $ botan_x509_cert_store_sql_affirm_cert- store_ptr- cert_ptr---- -- NOTE: See notes about returning arrays of things, improper return pointer type--- TODO: Need some sort of allocArrayQuerying, or a mkArrayInit-x509CertStoreSQLGenerateCRLs :: X509CertStore -> IO [X509CRL]-x509CertStoreSQLGenerateCRLs store = withX509CertStorePtr store $ \ store_ptr -> do- let fn arrPtr szPtr = botan_x509_cert_store_sql_generate_crls arrPtr szPtr store_ptr- alloca $ \ szPtr -> do- code <- fn nullPtr szPtr- case code of- InsufficientBufferSpace -> do- sz <- fromIntegral <$> peek szPtr- allocaArray sz $ \ arrPtr -> do- throwBotanIfNegative_ $ fn arrPtr szPtr- crlPtrs <- peekArray sz arrPtr- -- NOTE: Cannot use mkInit because that performs an alloca and a peek,- -- whereas we have already performed that work here- forM crlPtrs $ \ crlPtr -> do- foreignPtr <- newForeignPtr botan_x509_crl_destroy crlPtr- return $ MkX509CRL foreignPtr- _ -> throwBotanError code--{--SQLite3 cert store-NOTE: Not confirmed to be implemented correctly C++-side--}--x509CertStoreSqlite3Create :: ByteString -> ByteString -> RNG -> ByteString -> IO X509CertStore-x509CertStoreSqlite3Create db_path passwd rng table_prefix = asCString db_path $ \ db_path_ptr -> do- asCString passwd $ \ passwd_ptr -> do- withRNG rng $ \ botanRNG -> do- asCString table_prefix $ \ table_prefix_ptr -> mkInit- MkX509CertStore- (\ ptr -> botan_x509_cert_store_sqlite3_create- ptr- db_path_ptr- passwd_ptr botanRNG- table_prefix_ptr- )- botan_x509_cert_store_destroy--{--System cert store--}--x509CertStoreSystemCreate :: IO X509CertStore-x509CertStoreSystemCreate = mkInit MkX509CertStore botan_x509_cert_store_system_create botan_x509_cert_store_destroy--{--MacOS cert store-NOTE: OS-specific, covered by System certificate store type?--}---- foreign import ccall unsafe botan_x509_cert_store_macos_create--- :: Ptr X509CertStorePtr--- -> IO BotanErrorCode--{--Windows cert store-NOTE: OS-specific, covered by System certificate store type?--}---- foreign import ccall unsafe botan_x509_cert_store_windows_create--- :: Ptr X509CertStorePtr--- -> IO BotanErrorCode
src/Botan/Low/ZFEC.hs view
@@ -14,33 +14,35 @@ -} -module Botan.Low.ZFEC-(---- * Forward Error Correction--- $introduction--- * Usage--- $usage+{-# LANGUAGE OverloadedStrings #-} --- * ZFEC- ZFECShare(..)-, zfecEncode-, zfecDecode+module Botan.Low.ZFEC ( -) where+ -- * Forward Error Correction+ -- $introduction+ -- * Usage+ -- $usage -import Control.Concurrent+ -- * ZFEC+ ZFECShare+ , zfecEncode+ , zfecDecode -import Data.Foldable+ ) where +import Botan.Bindings.ConstPtr (ConstPtr (..))+import Botan.Bindings.ZFEC+import Botan.Low.Error.Internal+import Botan.Low.Internal.ByteString+import Botan.Low.Make+import Control.DeepSeq+import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Unsafe as ByteString--import Botan.Bindings.ZFEC--import Botan.Low.Error-import Botan.Low.Make-import Botan.Low.Prelude+import Data.Word+import Foreign.C.Types+import Foreign.Marshal.Alloc+import Foreign.Marshal.Array+import Foreign.Ptr {- $introduction
− test/Botan/Low/BcryptSpec.hs
@@ -1,25 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.RNG-import Botan.Low.Bcrypt--password :: ByteString-password = "Fee fi fo fum!"--factor = 12--main :: IO ()-main = hspec $ do- describe "bcryptGenerate" $ do- it "generates a bcrypt hash" $ do- rng <- rngInit "user-threadsafe"- _ <- bcryptGenerate password rng factor- pass- describe "bcryptIsValid" $ do- it "validates a bcrypt hash" $ do- rng <- rngInit "user-threadsafe"- h <- bcryptGenerate password rng factor- valid <- bcryptIsValid password h- valid `shouldBe` True
− test/Botan/Low/BlockCipherSpec.hs
@@ -1,68 +0,0 @@-module Main where--import Test.Prelude--import qualified Data.ByteString.Char8 as Char8-import qualified Data.ByteString as ByteString--import Botan.Low.BlockCipher-import Botan.Low.RNG--message = "Fee fi fo fum! I smell the blood of an Englishman!"--main :: IO ()-main = hspec $ testSuite allBlockCiphers chars $ \ bc -> do- it "can initialize a block cipher context" $ do- ctx <- blockCipherInit bc- pass- it "has a name" $ do- ctx <- blockCipherInit bc- name <- blockCipherName ctx- -- name `shouldBe` bc -- Name expands to include default parameters - need to record- pass- it "has a key spec" $ do- ctx <- blockCipherInit bc- (_,_,_) <- blockCipherGetKeyspec ctx- pass- it "has a block size" $ do- ctx <- blockCipherInit bc- _ <- blockCipherBlockSize ctx- pass- it "can set the key" $ do- ctx <- blockCipherInit bc- (_,mx,_) <- blockCipherGetKeyspec ctx- k <- systemRNGGet mx- blockCipherSetKey ctx k- pass- it "can encipher a message" $ do- ctx <- blockCipherInit bc- (_,mx,_) <- blockCipherGetKeyspec ctx- k <- systemRNGGet mx- blockCipherSetKey ctx k- bsz <- blockCipherBlockSize ctx- msg <- systemRNGGet $ bsz * 10- encmsg <- blockCipherEncryptBlocks ctx msg- pass- -- NOTE: It does not actually throw an error - this is slightly concerning.- -- it "can only encipher messages that are a multiple of the block size" $ do- -- ctx <- blockCipherInitName bc- -- bsz <- blockCipherBlockSize ctx- -- if bsz == 1- -- then pass- -- else do- -- (_,mx,_) <- blockCipherGetKeyspec ctx- -- k <- systemRNGGetIO mx- -- blockCipherSetKey ctx k- -- msg <- systemRNGGetIO $ bsz + 1- -- blockCipherEncryptBlocks ctx msg `shouldThrow` anyBotanException- it "can decipher an enciphered message" $ do- ctx <- blockCipherInit bc- (_,mx,_) <- blockCipherGetKeyspec ctx- k <- systemRNGGet mx- blockCipherSetKey ctx k- bsz <- blockCipherBlockSize ctx- msg <- systemRNGGet $ bsz * 10- encmsg <- blockCipherEncryptBlocks ctx msg- decmsg <- blockCipherDecryptBlocks ctx encmsg- decmsg `shouldBe` msg- pass
− test/Botan/Low/CipherSpec.hs
@@ -1,223 +0,0 @@-{-# OPTIONS_GHC -Wno-deprecations #-}--module Main where--import Test.Prelude--import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Char8 as Char8--import Botan.Bindings.Cipher-import Botan.Low.BlockCipher-import Botan.Low.Cipher-import Botan.Low.RNG--showBytes :: (Show a) => a -> ByteString-showBytes = Char8.pack . show--main :: IO ()-main = hspec $ testSuite (cipherModes ++ aeads) chars $ \ cipher -> do- it "can initialize a cipher encryption context" $ do- ctx <- cipherInit cipher Encrypt- pass- it "can initialize a cipher decryption context" $ do- ctx <- cipherInit cipher Decrypt- pass- it "has a name" $ do- ctx <- cipherInit cipher Encrypt- name <- cipherName ctx- -- name `shouldBe` bc -- Name expands to include default parameters - need to record- pass- it "has a key spec" $ do- ctx <- cipherInit cipher Encrypt- (_,_,_) <- cipherGetKeyspec ctx- pass- it "can set the key" $ do- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- pass- it "has a valid default nonce length" $ do- ctx <- cipherInit cipher Encrypt- nlen <- cipherGetDefaultNonceLength ctx- pass- it "can validate nonce length" $ do- ctx <- cipherInit cipher Encrypt- nlen <- cipherGetDefaultNonceLength ctx- defaultIsValid <- cipherValidNonceLength ctx nlen- defaultIsValid `shouldBe` True- -- NOTE: Some ciphers accept nonces of any length, eg SIV- -- Others allow any length > 0, eg GCM- -- Some have ranges- -- Some have just 'if (length == 0) return false;' eg OCB- -- Search C++ source for 'valid_nonce_length' to find them all- -- zeroIsValid <- cipherValidNonceLength ctx 0- -- zeroIsValid `shouldBe` False- -- absurdlyLargeIsValid <- cipherValidNonceLength ctx maxBound- -- absurdlyLargeIsValid `shouldBe` False- pass- it "can calculate output length" $ do- ctx <- cipherInit cipher Encrypt- olen <- cipherOutputLength ctx 1024- pass- -- NOTE: This check should be ae / aead only- it "has a tag length" $ do- ctx <- cipherInit cipher Encrypt- _ <- cipherGetTagLength ctx- pass- it "has an update graularity" $ do- ctx <- cipherInit cipher Encrypt- _ <- cipherGetUpdateGranularity ctx- pass- it "has an ideal update granularity" $ do- ctx <- cipherInit cipher Encrypt- _ <- cipherGetIdealUpdateGranularity ctx- pass- if cipher `elem` aeads- then it "can set the associated data" $ do- ctx <- cipherInit cipher Encrypt- -- NOTE: Undocumented: A cipher must set the key before setting AD- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- -- END NOTE- ad <- systemRNGGet 64- cipherSetAssociatedData ctx ad- pass- else pass- it "can reset all message state" $ do- ctx <- cipherInit cipher Encrypt- cipherReset ctx- pass- it "can clear all internal state" $ do- ctx <- cipherInit cipher Encrypt- cipherClear ctx- pass- it "can start processing a message" $ do- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- if cipher `elem` aeads- then do- ad <- systemRNGGet 64- cipherSetAssociatedData ctx ad- else pass- n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx- cipherStart ctx n- pass- -- TODO: More extensive testing in Botan; these are just binding sanity checks- it "can one-shot / offline encipher a message" $ do- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- if cipher `elem` aeads- then do- ad <- systemRNGGet 64- cipherSetAssociatedData ctx ad- else pass- n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx- cipherStart ctx n- g <- cipherGetIdealUpdateGranularity ctx- msg <- systemRNGGet g- encmsg <- cipherEncrypt ctx msg- pass- it "can one-shot / offline decipher a message" $ do- -- Same as prior test- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- ad <- systemRNGGet 64- if cipher `elem` aeads- then do- cipherSetAssociatedData ctx ad- else pass- n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx- cipherStart ctx n- g <- cipherGetIdealUpdateGranularity ctx- msg <- systemRNGGet g- encmsg <- cipherEncrypt ctx msg- -- Start actual test- dctx <- cipherInit cipher Decrypt- cipherSetKey dctx k- if cipher `elem` aeads- then do- cipherSetAssociatedData dctx ad- else pass- cipherStart dctx n- decmsg <- cipherDecrypt dctx encmsg- msg `shouldBe` decmsg- pass- it "can incrementally / online encipher a message" $ do- -- Same as prior test- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- ad <- systemRNGGet 64- if cipher `elem` aeads- then do- cipherSetAssociatedData ctx ad- else pass- n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx- cipherStart ctx n- g <- cipherGetIdealUpdateGranularity ctx- msg <- systemRNGGet (8 * g)- encmsg <- cipherEncryptOnline ctx msg- pass- -- NOTE: Fails for SIV, CCM because they are offline-only algorithms- -- This is expected, but not reflected in the tests yet- it "can incrementally / online decipher a message" $ do- ctx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec ctx- k <- systemRNGGet mx- cipherSetKey ctx k- ad <- systemRNGGet 64- if cipher `elem` aeads- then do- cipherSetAssociatedData ctx ad- else pass- n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx- cipherStart ctx n- g <- cipherGetIdealUpdateGranularity ctx- msg <- systemRNGGet (8 * g)- encmsg <- cipherEncryptOnline ctx msg- -- Start actual test- dctx <- cipherInit cipher Decrypt- cipherSetKey dctx k- if cipher `elem` aeads- then do- cipherSetAssociatedData dctx ad- else pass- cipherStart dctx n- decmsg <- cipherDecryptOnline dctx encmsg- msg `shouldBe` decmsg- pass- -- NOTE: Fails for SIV, CCM because they are offline-only algorithms- -- This is expected, but not reflected in the tests yet- it "has parity between online and offline" $ do- onlinectx <- cipherInit cipher Encrypt- offlinectx <- cipherInit cipher Encrypt- (_,mx,_) <- cipherGetKeyspec onlinectx- k <- systemRNGGet mx- cipherSetKey onlinectx k- cipherSetKey offlinectx k- ad <- systemRNGGet 64- when (cipher `elem` aeads) $ do- cipherSetAssociatedData onlinectx ad- cipherSetAssociatedData offlinectx ad- n <- systemRNGGet =<< cipherGetDefaultNonceLength onlinectx- cipherStart onlinectx n- cipherStart offlinectx n- g <- cipherGetIdealUpdateGranularity onlinectx- msg <- systemRNGGet (8 * g)- putStrLn " Testing online encryption:"- onlinemsg <- cipherEncryptOnline onlinectx msg- putStrLn " Testing offline encryption:"- offlinemsg <- cipherEncrypt offlinectx msg- putStrLn " Result:"- onlinemsg `shouldBe` offlinemsg- pass
− test/Botan/Low/FPESpec.hs
@@ -1,97 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.FPE-import Botan.Bindings.FPE-import Botan.Low.MPI-import Botan.Low.RNG--import Botan.Low.Error---- NOTE: FPE operations encrypt/decrypt integers less than n-nStr :: ByteString-nStr = "1000000000000"--key :: ByteString-key = "0000DEADBEEF0000"--rounds :: Int-rounds = 3--newRng :: IO RNG-newRng = rngInit "system"--newX :: MP -> IO MP-newX n = do- x <- mpInit- z <- mpInit- mpSetFromInt z 0- -- n <- mpInit- -- mpSetFromStr n nStr- rng <- newRng- mpRandRange x rng z n- return x--newFE1Ctx :: IO (MP, FPE)-newFE1Ctx = do- n <- mpInit- mpSetFromStr n nStr- ctx <- fpeInitFE1 n key rounds BOTAN_FPE_FLAG_NONE- return (n,ctx)--newFE1CtxCompatMode :: IO (MP, FPE)-newFE1CtxCompatMode = do- n <- mpInit- mpSetFromStr n nStr- ctx <- fpeInitFE1 n key rounds BOTAN_FPE_FLAG_FE1_COMPAT_MODE- return (n,ctx)--main :: IO ()-main = hspec $ do- describe "fpeInitFE1" $ do- it "initializes an FE1 FPE context" $ do- newFE1Ctx- pass- it "initializes an FE1 FPE context in compat mode" $ (do- newFE1CtxCompatMode- pass- ) -- `shouldThrow` anyBotanException- describe "fpeEncrypt" $ do- it "performs format-preserving encryption" $ do- (n,ctx) <- newFE1Ctx- x <- newX n- y <- mpCopy x- fpeEncrypt ctx y ""- isSame <- mpEqual x y- isSame `shouldBe` False- pass- it "performs format-preserving encryption in compat mode" $ do- (n,ctx) <- newFE1CtxCompatMode- x <- newX n- y <- mpCopy x- fpeEncrypt ctx y ""- isSame <- mpEqual x y- isSame `shouldBe` False- pass- describe "fpeDecrypt" $ do- it "performs format-preserving decryption" $ do- (n,ctx) <- newFE1Ctx- x <- newX n- y <- mpCopy x- fpeEncrypt ctx y ""- z <- mpCopy y- fpeDecrypt ctx z ""- isSame <- mpEqual x z- isSame `shouldBe` True- pass- it "performs format-preserving decryption in compat mode" $ do- (n,ctx) <- newFE1CtxCompatMode- x <- newX n- y <- mpCopy x- fpeEncrypt ctx y ""- z <- mpCopy y- fpeDecrypt ctx z ""- isSame <- mpEqual x z- isSame `shouldBe` True- pass
− test/Botan/Low/HOTPSpec.hs
@@ -1,27 +0,0 @@-module Main where--import Test.Prelude-import Botan.Low.Hash (HashName(..))--import Botan.Low.HOTP--key :: ByteString-key = "Fee fi fo fum"--counter :: HOTPCounter-counter = 12345--main :: IO ()-main = hspec $ testSuite hotpHashes chars $ \ h -> do- it "hotpInit" $ do- ctx <- hotpInit key h 6- pass- it "hotpGenerate" $ do- ctx <- hotpInit key h 6- code <- hotpGenerate ctx counter- pass- it "hotpCheck" $ do- ctx <- hotpInit key h 6- code <- hotpGenerate ctx counter- (success,next) <- hotpCheck ctx code counter 0- success `shouldBe` True
− test/Botan/Low/HashSpec.hs
@@ -1,48 +0,0 @@-module Main where--import Test.Prelude--import qualified Data.ByteString.Char8 as Char8-import qualified Data.ByteString as ByteString--import Botan.Bindings.Hash-import Botan.Low.Hash--message = "Fee fi fo fum! I smell the blood of an Englishman!"--main :: IO ()-main = hspec $ testSuite allHashes chars $ \ h -> do- it "can initialize a hash context" $ do- ctx <- hashInit h- pass- it "has a name" $ do- ctx <- hashInit h- name <- hashName ctx- pass- it "has an output length" $ do- ctx <- hashInit h- olen <- hashOutputLength ctx- pass- it "can copy the internal state" $ do- ctx <- hashInit h- -- TODO: Populate with state and actually prove- ctx' <- hashCopyState ctx- pass- it "can clear all internal state" $ do- ctx <- hashInit h- -- TODO: Populate with state and actually prove- hashClear ctx- pass- it "can update the internal state with a single message block" $ do- ctx <- hashInit h- hashUpdate ctx message- pass- it "can update the internal state with multiple message blocks" $ do- ctx <- hashInit h- forM_ (splitBlocks 4 message) $ hashUpdate ctx- pass- it "can finalize a digest" $ do- ctx <- hashInit h- hashUpdate ctx message- d <- hashFinal ctx- pass
− test/Botan/Low/KDFSpec.hs
@@ -1,34 +0,0 @@-module Main where--import Test.Prelude--import qualified Data.ByteString as ByteString---- import Botan.Low.Hash-import Botan.Low.KDF---- NOTE: Many of these tests fail because different kdfs / macs / hashes have different requirements--- such as input or output lengths.--- Failing tests:--- - Checksums: Adler32, CRC24, CRC32, BadParameterException for almost everything--- - Probably because they are not cryptographic hashes--- - All fail for HKDF, SP800-108-*(HMAC(hash)), SP800-56A(hash), SP800-56A(HMAC(hash)), SP800-56C(hash), SP800-56C(HMAC(hash))--- - CRC24 fails for KDF1, probably because we asked for 4 bits--- - HKDF-Extract - BadParameterException for unknown reasons--- - X9.42-PRF: About half fail with NotImplementedException--- - SP800-108-*: Requires HMAC wrapping, may support other MACs?--- - SP800-56A / SP800-56C: Allow HMAC wrapping, may support other MACs?--- Many do accept HMAC(hash) as an argument instead of just hash--- It is not yet known which if any MACs work aside from HMAC (eg whether anything with CMAC, GMAC does) due to non-exhaustive--- enumeration of MACs in their own tests.--- SP800 KDFs fail with InvalidKeyLengthException for X9.19-MAC, Poly1305, SipHash(2,4)--- except for SP800-56A which fails with NotImplementedException--- TODO: Exhaustive algorithm testing (see Botan.KDF notes)---- NOTE: Some kdfs (HKDF-Extract) do not support label input arguments-main :: IO ()-main = hspec $ testSuite kdfs chars $ \ algo -> do- describe "kdf" $ do- it "can derive a key" $ do- key <- kdf algo 3 "secret" "salt" "" -- "label"- pass
− test/Botan/Low/KeyWrapSpec.hs
@@ -1,42 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.BlockCipher--import Botan.Low.RNG--import Botan.Low.KeyWrap--kwKey :: ByteString-kwKey = "This key *must* be a multiple of 8 bytes"--kwpKey :: ByteString-kwpKey = "Fee fi fo fum! I smell the blood of an Englishman!"--main :: IO ()-main = hspec $ testSuite blockCipher128s chars $ \ algo -> do- it "nistKeyWrapEncode" $ do- (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo- kek <- systemRNGGet keklen- wrappedKey <- nistKeyWrapEncode algo 0 kwKey kek- pass- it "nistKeyWrapDecode" $ do- (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo- kek <- systemRNGGet keklen- wrappedKey <- nistKeyWrapEncode algo 0 kwKey kek- unwrappedKey <- nistKeyWrapDecode algo 0 wrappedKey kek- unwrappedKey `shouldBe` kwKey- pass- it "nistKeyWrapEncode padded" $ do- (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo- kek <- systemRNGGet keklen- wrappedKey <- nistKeyWrapEncode algo 1 kwpKey kek- pass- it "nistKeyWrapDecode padded" $ do- (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo- kek <- systemRNGGet keklen- wrappedKey <- nistKeyWrapEncode algo 1 kwpKey kek- unwrappedKey <- nistKeyWrapDecode algo 1 wrappedKey kek- unwrappedKey `shouldBe` kwpKey- pass
− test/Botan/Low/MACSpec.hs
@@ -1,132 +0,0 @@-module Main where--import Test.Prelude--import Botan.Bindings.MAC-import Botan.Low.MAC--import Botan.Low.RNG---- NOTE: Full testing of all algorithms will require botan ADT types instead--- of names, to be able to query for acceptable key and nonce parameters---- nonceMACs :: [(MACName, Maybe Int)]--- nonceMACs = concat--- [ [ "CMAC(" <> bc <> ")" | bc <- allBlockCiphers ]--- , [ "GMAC(" <> bc <> ")" | bc <- allBlockCiphers ]--- , [ "CBC-MAC(" <> bc <> ")" | bc <- allBlockCiphers ]--- ]---- macs :: [(MACName, Maybe Int)]--- macs = concat--- [ nonceMACs--- , [ "HMAC(" <> h <> ")" | h <- hashes ]--- , [ "Poly1305", "SipHash(2,4)", "X9.19-MAC" ]--- ]--macs :: [MACName]-macs = fmap fst macsAndNonceSzs--macsAndNonceSzs :: [(MACName, Int)]-macsAndNonceSzs =- [ ("CMAC(AES-256)", 0)- , ("GMAC(AES-256)", 16)- -- , ("CBC-MAC(AES-256)", 16) -- Not supported- , ("HMAC(SHA-3)", 0)- , ("Poly1305", 0)- , ("SipHash(2,4)", 0)- , ("X9.19-MAC", 0)- ]--message :: ByteString-message = "Fee fi fo fum! I smell the blood of an Englishman!"--main :: IO ()-main = hspec $ testSuite macsAndNonceSzs (chars . fst) $ \ (h,nonceSz) -> do- it "can initialize a mac context" $ do- ctx <- macInit h- pass- it "has a name" $ do- ctx <- macInit h- name <- macName ctx- pass- it "has an output length" $ do- ctx <- macInit h- olen <- macOutputLength ctx- pass- -- it "can copy the internal state" $ do- -- ctx <- macInit h- -- -- TODO: Populate with state and actually prove- -- ctx' <- macCopyState ctx- -- pass- it "has a key spec" $ do- ctx <- macInit h- (_,_,_) <- macGetKeyspec ctx- pass- it "can set the key" $ do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- pass- -- it "has a nonce spec" $ do- it "can set the nonce" $ do- if nonceSz > 0- then do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- n <- systemRNGGet nonceSz- macSetNonce ctx n- else pass- it "can clear all internal state" $ do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- if nonceSz > 0- then do- n <- systemRNGGet nonceSz- macSetNonce ctx n- else pass- -- TODO: Populate with state and actually prove- macClear ctx- pass- it "can update the internal state with a single message block" $ do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- if nonceSz > 0- then do- n <- systemRNGGet nonceSz- macSetNonce ctx n- else pass- macUpdate ctx message- pass- it "can update the internal state with multiple message blocks" $ do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- if nonceSz > 0- then do- n <- systemRNGGet nonceSz- macSetNonce ctx n- else pass- forM_ (splitBlocks 4 message) $ macUpdate ctx- pass- it "can finalize a digest" $ do- ctx <- macInit h- (_,mx,_) <- macGetKeyspec ctx- k <- systemRNGGet mx- macSetKey ctx k- if nonceSz > 0- then do- n <- systemRNGGet nonceSz- macSetNonce ctx n- else pass- macUpdate ctx message- d <- macFinal ctx- pass
− test/Botan/Low/MPISpec.hs
@@ -1,382 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG---- NOTE: These unit tests are mostly checking that the functions are bound correctly.--- These are kind of crappy and repetative, but that also exposes what needs to be done--- in the higher libraries to make things ergonomic.--main :: IO ()-main = hspec $ do- it "can initialize a mutable MPI reference" $ do- mp <- mpInit- pass- it "can set the value from an int" $ do- mp <- mpInit- mpSetFromInt mp 512- pass- it "can set the value from another reference" $ do- mp <- mpInit- mpSetFromInt mp 512- mp' <- mpInit- mpSetFromMP mp' mp- isEqual <- mpEqual mp mp'- isEqual `shouldBe` True- pass- it "can be converted to a hex string" $ do- mp <- mpInit- mpSetFromInt mp 512- hex <- mpToHex mp- hex `shouldBe` "0x0200"- hex' <- mpToStr mp 16- hex' `shouldBe` "0x0200"- pass- it "can be converted to a decimal string" $ do- mp <- mpInit- mpSetFromInt mp 512- dec <- mpToStr mp 10- dec `shouldBe` "512"- pass- it "can be cleared to zero" $ do- mp <- mpInit- mpSetFromInt mp 512- mpClear mp- zero <- mpInit- mpSetFromInt zero 0- isClear <- mpEqual mp zero- isClear `shouldBe` True- pass- it "can copy the value to a new reference" $ do- mp <- mpInit- mpSetFromInt mp 512- mp' <- mpCopy mp- isEqual <- mpEqual mp mp'- isEqual `shouldBe` True- pass- it "can set the value from a decimal string" $ do- mp <- mpInit- mpSetFromStr mp "512"- mp' <- mpInit- mpSetFromInt mp' 512- isEqualStr <- mpEqual mp mp'- isEqualStr `shouldBe` True- mpSetFromRadixStr mp "512" 10- isEqualRadixStr <- mpEqual mp mp'- isEqualRadixStr `shouldBe` True- pass- it "can set the value from a hex string" $ do- mp <- mpInit- mpSetFromStr mp "0x0200"- mp' <- mpInit- mpSetFromInt mp' 512- isEqualStr <- mpEqual mp mp'- isEqualStr `shouldBe` True- mpSetFromRadixStr mp "0200" 16 -- NOTE: Lack of "0x" prefix- isEqualRadixStr <- mpEqual mp mp'- isEqualRadixStr `shouldBe` True- pass- it "has a number of bits" $ do- mp <- mpInit- mpSetFromStr mp "512"- n <- mpNumBits mp- n `shouldBe` 10- pass- it "has a number of bytes" $ do- mp <- mpInit- mpSetFromStr mp "512"- n <- mpNumBytes mp- n `shouldBe` 2- pass- it "can be converted to a bytestring" $ do- mp <- mpInit- mpSetFromInt mp 512- bin <- mpToBin mp- bin `shouldBe` "\STX\NUL" -- NOTE: 0b 0000 0010 0000 0000- pass- it "can set the value from a bytestring" $ do- mp <- mpInit- mpFromBin mp "\STX\NUL"- mp' <- mpInit- mpSetFromInt mp' 512- isEqual <- mpEqual mp mp'- isEqual `shouldBe` True- pass- it "can be converted to a 32-bit word" $ do- mp <- mpInit- mpSetFromInt mp 512- w <- mpToWord32 mp- w `shouldBe` 512- pass- -- NOTE: Returns whether x >= 0, not x > 0 because 0 is treated as positive.- it "can check if a value is positive" $ do- n <- mpInit- mpSetFromInt n (-512)- nIsPositive <- mpIsPositive n- nIsPositive `shouldBe` False- z <- mpInit- mpSetFromInt z 0- zIsPositive <- mpIsPositive z- zIsPositive `shouldBe` True- p <- mpInit- mpSetFromInt p 512- pIsPositive <- mpIsPositive p- pIsPositive `shouldBe` True- pass- it "can check if a value is negative" $ do- n <- mpInit- mpSetFromInt n (-512)- nIsPositive <- mpIsNegative n- nIsPositive `shouldBe` True- z <- mpInit- mpSetFromInt z 0- zIsPositive <- mpIsNegative z- zIsPositive `shouldBe` False- p <- mpInit- mpSetFromInt p 512- pIsPositive <- mpIsNegative p- pIsPositive `shouldBe` False- pass- it "can flip the sign of a value" $ do- p <- mpInit- mpSetFromInt p 512- n <- mpInit- mpSetFromInt n (-512)- mpFlipSign p- isEqual <- mpEqual p n- pass- it "can check whether a value is zero" $ do- mp <- mpInit- mpSetFromInt mp 512- isZeroA <- mpIsZero mp- isZeroA `shouldBe` False- mpSetFromInt mp 0- isZeroB <- mpIsZero mp- isZeroB `shouldBe` True- mpSetFromInt mp (-512)- isZeroC <- mpIsZero mp- isZeroC `shouldBe` False- pass- it "can add a 32-bit word to the value and store the result" $ do- mp <- mpInit- mpSetFromInt mp 512- mp' <- mpInit- mpAddWord32 mp' mp 512- result <- mpToWord32 mp'- result `shouldBe` 1024- pass- it "can subtract a 32-bit word from the value and store the result" $ do- mp <- mpInit- mpSetFromInt mp 512- mp' <- mpInit- mpSubWord32 mp' mp 512- result <- mpToWord32 mp'- result `shouldBe` 0- pass- it "can add one value to another and store the result" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 512- c <- mpInit- mpAdd c a b- result <- mpToWord32 c- result `shouldBe` 1024- pass- it "can subtract one value from another and store the result" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 512- c <- mpInit- mpSub c a b- result <- mpToWord32 c- result `shouldBe` 0- pass- it "can multiply one value by another and store the result" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 512- c <- mpInit- mpMul c a b- result <- mpToWord32 c- result `shouldBe` 262144- pass- it "can divide one value by another and store the result" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 512- q <- mpInit- r <- mpInit- mpDiv q r a b- quotient <- mpToWord32 q- quotient `shouldBe` 1- remainder <- mpToWord32 r- remainder `shouldBe` 0- pass- it "can perform multiplication and modulus at the same time and store the result" $ do- r <- mpInit- x <- mpInit- mpSetFromInt x 2- y <- mpInit- mpSetFromInt y 4- mod <- mpInit- mpSetFromInt mod 7- mpModMul r x y mod- result <- mpToWord32 r- result `shouldBe` 1- pass- it "can check whether two values are equal" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 512- c <- mpInit- mpSetFromInt c 256- abEqual <- mpEqual a b- abEqual `shouldBe` True- bcEqual <- mpEqual b c- bcEqual `shouldBe` False- pass- it "can compare two values for order" $ do- n <- mpInit- mpSetFromInt n (-1)- z <- mpInit- mpSetFromInt z 0- p <- mpInit- mpSetFromInt p 1- nz <- mpCmp n z- nz `shouldBe` -1- zp <- mpCmp z p- zp `shouldBe` -1- np <- mpCmp n p- np `shouldBe` -1- nn <- mpCmp n n- nn `shouldBe` 0- zz <- mpCmp z z- zz `shouldBe` 0- pp <- mpCmp p p- pp `shouldBe` 0- pz <- mpCmp p z- pz `shouldBe` 1- zn <- mpCmp z n- zn `shouldBe` 1- pn <- mpCmp p n- pn `shouldBe` 1- pass- it "can swap the values of two references" $ do- a <- mpInit- mpSetFromInt a 512- b <- mpInit- mpSetFromInt b 256- mpSwap a b- a' <- mpToWord32 a- a' `shouldBe` 256- b' <- mpToWord32 b- b' `shouldBe` 512- pass- it "can perform exponentiation and modulus at the same time and store the result" $ do- r <- mpInit- x <- mpInit- mpSetFromInt x 2- y <- mpInit- mpSetFromInt y 4- mod <- mpInit- mpSetFromInt mod 7- mpPowMod r x y mod- result <- mpToWord32 r- result `shouldBe` 2- pass- it "can perform a left shift and store the result" $ do- mp <- mpInit- result <- mpInit- mpSetFromInt mp 512- mpLeftShift result mp 3- r <- mpToWord32 result- r `shouldBe` 4096- pass- it "can perform a right shift and store the result" $ do- mp <- mpInit- result <- mpInit- mpSetFromInt mp 512- mpRightShift result mp 3- r <- mpToWord32 result- r `shouldBe` 64- pass- it "can compute the modular inverse" $ do- result <- mpInit- x <- mpInit- mpSetFromInt x 29- mod <- mpInit- mpSetFromInt mod 65535- -- x needs to be smaller than mod- mpModInverse result x mod- r <- mpToWord32 result- r `shouldBe` 13559- pass- it "can generate a number of random bits" $ do- mp <- mpInit- rng <- rngInit "system"- mpRandBits mp rng 9- r <- mpToWord32 mp- 0 <= r && r < 512 `shouldBe` True- pass- it "can generate a random value within a range [lower,upper)" $ do- result <- mpInit- rng <- rngInit "system"- lower <- mpInit- mpSetFromInt lower 4- upper <- mpInit- mpSetFromInt upper 8- mpRandRange result rng lower upper- isGTELower <- (<= 0) <$> mpCmp lower result- isGTELower `shouldBe` True- isLTUpper <- (== -1) <$> mpCmp result upper- isLTUpper `shouldBe` True- pass- it "can calculate the greatest common divisor of a value" $ do- r <- mpInit- x <- mpInit- mpSetFromInt x 35- y <- mpInit- mpSetFromInt y 21- mpGCD r x y- result <- mpToWord32 r- result `shouldBe` 7- pass- it "can probabilistically calculate whether a value is prime" $ do- mp <- mpInit- rng <- rngInit "system"- mpSetFromInt mp 131- isPrime131 <- mpIsPrime mp rng 100- isPrime131 `shouldBe` True- mpSetFromInt mp 133- isPrime133 <- mpIsPrime mp rng 100- isPrime133 `shouldBe` False- pass- it "can get the value of a single bit" $ do- mp <- mpInit- mpSetFromInt mp 512- f <- mpGetBit mp 8- f `shouldBe` False- t <- mpGetBit mp 9- t `shouldBe` True- pass- it "can set the value of a single bit (set it to 1)" $ do- mp <- mpInit- mpSetFromInt mp 512- mpSetBit mp 8- result <- mpToWord32 mp- result `shouldBe` 768- pass- it "can clear the value of a single bit (set it to 0)" $ do- mp <- mpInit- mpSetFromInt mp 768- mpClearBit mp 8- result <- mpToWord32 mp- result `shouldBe` 512- pass
− test/Botan/Low/PubKey/DHSpec.hs
@@ -1,69 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.DH---- TODO: Consolidate-dlGroups :: [ByteString]-dlGroups =- [ "ffdhe/ietf/2048"- , "ffdhe/ietf/3072"- , "ffdhe/ietf/4096"- , "ffdhe/ietf/6144"- , "ffdhe/ietf/8192"- , "modp/ietf/1024"- , "modp/ietf/1536"- , "modp/ietf/2048"- , "modp/ietf/3072"- , "modp/ietf/4096"- , "modp/ietf/6144"- , "modp/ietf/8192"- , "modp/srp/1024"- , "modp/srp/1536"- , "modp/srp/2048"- , "modp/srp/3072"- , "modp/srp/4096"- , "modp/srp/6144"- , "modp/srp/8192"- , "dsa/jce/1024"- , "dsa/botan/2048"- , "dsa/botan/3072"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp--main :: IO ()-main = hspec $ testSuite dlGroups chars $ \ dlGroup -> do- it "privKeyLoadDH" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- p <- privKeyField privKey "p"- g <- privKeyField privKey "g"- x <- privKeyField privKey "x"- loadedKey <- privKeyLoadDH p g x- pass- it "pubKeyLoadDH" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- pubKey <- privKeyExportPubKey privKey- p <- pubKeyField pubKey "p"- g <- pubKeyField pubKey "g"- y <- pubKeyField pubKey "y"- loadedKey <- pubKeyLoadDH p g y- pass
− test/Botan/Low/PubKey/DSASpec.hs
@@ -1,76 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.DSA---- TODO: Consolidate-dlGroups :: [ByteString]-dlGroups =- [ "ffdhe/ietf/2048"- , "ffdhe/ietf/3072"- , "ffdhe/ietf/4096"- , "ffdhe/ietf/6144"- , "ffdhe/ietf/8192"- , "modp/ietf/1024"- , "modp/ietf/1536"- , "modp/ietf/2048"- , "modp/ietf/3072"- , "modp/ietf/4096"- , "modp/ietf/6144"- , "modp/ietf/8192"- , "modp/srp/1024"- , "modp/srp/1536"- , "modp/srp/2048"- , "modp/srp/3072"- , "modp/srp/4096"- , "modp/srp/6144"- , "modp/srp/8192"- , "dsa/jce/1024"- , "dsa/botan/2048"- , "dsa/botan/3072"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp---- NOTE: Fields are--- DLGroup PubKey: p, q, g, y--- DLGroup PrivKey: p, q, g, x, y---- NOTE: Fails on pubKeyLoadDSA for srp curves (but not privKeyLoadDSA?)-main :: IO ()-main = hspec $ testSuite dlGroups chars $ \ dlGroup -> do- it "privKeyLoadDSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- p <- privKeyField privKey "p"- q <- privKeyField privKey "q"- g <- privKeyField privKey "g"- x <- privKeyField privKey "x"- loadedKey <- privKeyLoadDSA p q g x- pass- it "pubKeyLoadDSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- pubKey <- privKeyExportPubKey privKey- p <- pubKeyField pubKey "p"- q <- pubKeyField pubKey "q"- g <- pubKeyField pubKey "g"- y <- pubKeyField pubKey "y"- loadedKey <- pubKeyLoadDSA p q g y- pass
− test/Botan/Low/PubKey/DecryptSpec.hs
@@ -1,43 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.PubKey-import Botan.Low.PubKey.Encrypt-import Botan.Low.PubKey.Decrypt-import Botan.Low.RNG--pks =- [ ("RSA", "2048", "PKCS1v15")- , ("SM2", "sm2p256v1", "SHA-256") -- NOTE: Decrypt fails with InsufficientBufferSpace- , ("ElGamal", "modp/ietf/1024", "PKCS1v15")- ]--pkTestName :: (ByteString, ByteString, ByteString) -> String-pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding--main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param, padding) -> do- it "decryptCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- decryptCreate privKey padding- pass- it "decryptOutputLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- decryptCreate privKey padding- _ <- decryptOutputLength ctx 128- pass- it "decrypt" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ectx <- encryptCreate pubKey padding- dctx <- decryptCreate privKey padding- encrypted <- encrypt ectx rng "Fee fi fo fum!"- decrypted <- decrypt dctx encrypted- decrypted `shouldBe` "Fee fi fo fum!"- pass
− test/Botan/Low/PubKey/ECDHSpec.hs
@@ -1,78 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.ECDH---- TODO: Consolidate-ecGroups :: [ByteString]-ecGroups =- [ "secp160k1"- , "secp160r1"- , "secp160r2"- , "secp192k1"- , "secp192r1"- , "secp224k1"- , "secp224r1"- , "secp256k1"- , "secp256r1"- , "secp384r1"- , "secp521r1"- , "brainpool160r1"- , "brainpool192r1"- , "brainpool224r1"- , "brainpool256r1"- , "brainpool320r1"- , "brainpool384r1"- , "brainpool512r1"- , "x962_p192v2"- , "x962_p192v3"- , "x962_p239v1"- , "x962_p239v2"- , "x962_p239v3"- , "gost_256A"- , "gost_512A"- , "frp256v1"- , "sm2p256v1"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp--main :: IO ()-main = hspec $ testSuite ecGroups chars $ \ ecGroup -> do- it "privKeyLoadECDH" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "ECDH" ecGroup rng- x <- privKeyField privKey "x"- loadedPrivKey <- privKeyLoadECDH x ecGroup- -- Inscrutible >_> but shows key equality- -- pubKeyBytes <- privKeyExportPubKey privKey >>= \ pubKey -> pubKeyExport pubKey PubKeyExportPEM- -- loadedPubKeyBytes <- privKeyExportPubKey loadedPrivKey >>= \ loadedPubKey -> pubKeyExport loadedPubKey PubKeyExportPEM- -- pubKeyBytes `shouldBe` loadedPubKeyBytes- pass- it "pubKeyLoadECDH" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "ECDH" ecGroup rng- pubKey <- privKeyExportPubKey privKey- public_x <- pubKeyField pubKey "public_x"- public_y <- pubKeyField pubKey "public_y"- loadedPubKey <- pubKeyLoadECDH public_x public_y ecGroup- -- pubKeyBytes <- pubKeyExport pubKey PubKeyExportPEM- -- loadedPubKeyBytes <- pubKeyExport loadedPubKey PubKeyExportPEM- -- pubKeyBytes `shouldBe` loadedPubKeyBytes- pass
− test/Botan/Low/PubKey/ECDSASpec.hs
@@ -1,72 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.ECDSA---- TODO: Consolidate-ecGroups :: [ByteString]-ecGroups =- [ "secp160k1"- , "secp160r1"- , "secp160r2"- , "secp192k1"- , "secp192r1"- , "secp224k1"- , "secp224r1"- , "secp256k1"- , "secp256r1"- , "secp384r1"- , "secp521r1"- , "brainpool160r1"- , "brainpool192r1"- , "brainpool224r1"- , "brainpool256r1"- , "brainpool320r1"- , "brainpool384r1"- , "brainpool512r1"- , "x962_p192v2"- , "x962_p192v3"- , "x962_p239v1"- , "x962_p239v2"- , "x962_p239v3"- , "gost_256A"- , "gost_512A"- , "frp256v1"- , "sm2p256v1"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp---- TODO: Don't assume just "ECDSA", other elliptic curve DSA might be valid here-main :: IO ()-main = hspec $ testSuite ecGroups chars $ \ ecGroup -> do- it "privKeyLoadECDSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "ECDSA" ecGroup rng- x <- privKeyField privKey "x"- loadedPrivKey <- privKeyLoadECDSA x ecGroup- pass- it "pubKeyLoadECDSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "ECDSA" ecGroup rng- pubKey <- privKeyExportPubKey privKey- public_x <- pubKeyField pubKey "public_x"- public_y <- pubKeyField pubKey "public_y"- loadedPubKey <- pubKeyLoadECDSA public_x public_y ecGroup- pass
− test/Botan/Low/PubKey/Ed25519Spec.hs
@@ -1,34 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.Ed25519--main :: IO ()-main = hspec $ do- it "privKeyEd25519GetPrivKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Ed25519" "" rng- exportedPrivKey <- privKeyEd25519GetPrivKey privKey- pass- it "pubKeyEd25519GetPubKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Ed25519" "" rng- pubKey <- privKeyExportPubKey privKey- exportedPubKey <- pubKeyEd25519GetPubKey pubKey- pass- it "privKeyLoadEd25519" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Ed25519" "" rng- exportedPrivKey <- privKeyEd25519GetPrivKey privKey- loadedPrivKey <- privKeyLoadEd25519 exportedPrivKey- pass- it "pubKeyLoadEd25519" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Ed25519" "" rng- pubKey <- privKeyExportPubKey privKey- exportedPubKey <- pubKeyEd25519GetPubKey pubKey- loadedPubKey <- pubKeyLoadEd25519 exportedPubKey- pass
− test/Botan/Low/PubKey/ElGamalSpec.hs
@@ -1,71 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.ElGamal---- TODO: Consolidate-dlGroups :: [ByteString]-dlGroups =- [ "ffdhe/ietf/2048"- , "ffdhe/ietf/3072"- , "ffdhe/ietf/4096"- , "ffdhe/ietf/6144"- , "ffdhe/ietf/8192"- , "modp/ietf/1024"- , "modp/ietf/1536"- , "modp/ietf/2048"- , "modp/ietf/3072"- , "modp/ietf/4096"- , "modp/ietf/6144"- , "modp/ietf/8192"- , "modp/srp/1024"- , "modp/srp/1536"- , "modp/srp/2048"- , "modp/srp/3072"- , "modp/srp/4096"- , "modp/srp/6144"- , "modp/srp/8192"- , "dsa/jce/1024"- , "dsa/botan/2048"- , "dsa/botan/3072"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp--main :: IO ()-main = hspec $ testSuite dlGroups chars $ \ dlGroup -> do- it "privKeyLoadELGamal" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- p <- privKeyField privKey "p"- q <- privKeyField privKey "q"- g <- privKeyField privKey "g"- x <- privKeyField privKey "x"- loadedKey <- privKeyLoadElGamal p g x- pass- it "pubKeyLoadELGamal" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "DH" dlGroup rng- pubKey <- privKeyExportPubKey privKey- p <- privKeyField privKey "p"- q <- privKeyField privKey "q"- g <- privKeyField privKey "g"- y <- privKeyField privKey "y"- loadedKey <- pubKeyLoadElGamal p g y- pass
− test/Botan/Low/PubKey/EncryptSpec.hs
@@ -1,42 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.PubKey-import Botan.Low.PubKey.Encrypt-import Botan.Low.RNG---- NOTE: SM2 encrypt fails with InsufficientBufferSpace unless sm2p256v1 is used as the--- curve when creating the key (but creating the key and the encryption context do not fail)--pks =- [ ("RSA", "2048", "PKCS1v15")- , ("SM2", "sm2p256v1", "SHA-256") -- NOTE: SM2 takes a hash rather than a padding- , ("ElGamal", "modp/ietf/1024", "PKCS1v15")- ]--pkTestName :: (ByteString, ByteString, ByteString) -> String-pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding--main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param, padding) -> do- it "encryptCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- encryptCreate pubKey padding- pass- it "encryptOutputLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- encryptCreate pubKey padding- _ <- encryptOutputLength ctx 128- pass- it "encrypt" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- encryptCreate pubKey padding- _ <- encrypt ctx rng "Fee fi fo fum!"- pass
− test/Botan/Low/PubKey/KeyAgreementSpec.hs
@@ -1,50 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.PubKey-import Botan.Low.PubKey.KeyAgreement-import Botan.Low.RNG---- TODO: More thorough test with different KDFs and curves / groups-pks :: [(ByteString, ByteString)]-pks =- [ ("DH", "modp/ietf/1024")- , ("ECDH", "secp256r1")- , ("Curve25519", "")- ]--pkTestName :: (ByteString, ByteString) -> String-pkTestName (pk, param) = chars $ pk <> " " <> param--main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param) -> do- it "keyAgreementCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ka <- keyAgreementCreate privKey "HKDF(SHA-256)"- pass- it "keyAgreementExportPublic" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ka <- keyAgreementCreate privKey "HKDF(SHA-256)"- pubKey <- keyAgreementExportPublic privKey- pass- it "keyAgreementSize" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ka <- keyAgreementCreate privKey "HKDF(SHA-256)"- kaSz <- keyAgreementSize ka- pass- it "keyAgreement" $ do- rng <- rngInit "system"- privKeyA <- privKeyCreate pk param rng- pubKeyA <- keyAgreementExportPublic privKeyA- privKeyB <- privKeyCreate pk param rng- pubKeyB <- keyAgreementExportPublic privKeyB- kaA <- keyAgreementCreate privKeyA "HKDF(SHA-256)"- kaB <- keyAgreementCreate privKeyB "HKDF(SHA-256)"- sharedKeyA <- keyAgreement kaA pubKeyB "salt"- sharedKeyB <- keyAgreement kaB pubKeyA "salt"- sharedKeyA `shouldBe` sharedKeyB- pass
− test/Botan/Low/PubKey/KeyEncapsulationSpec.hs
@@ -1,67 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.PubKey-import Botan.Low.PubKey.KeyEncapsulation-import Botan.Low.RNG--pks =- [ ( "RSA", "2048", "HKDF(SHA-256)")- , ( "Kyber", "", "HKDF(SHA-256)")- , ( "McEliece", "", "HKDF(SHA-256)")- ]--pkTestName :: (ByteString, ByteString, ByteString) -> String-pkTestName (pk, param, kdf) = chars $ pk <> " " <> param <> " " <> kdf--main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param, kdf) -> do- it "kemEncryptCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"- pass- it "kemEncryptSharedKeyLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"- sharedKeyLength <- kemEncryptSharedKeyLength kem 256- pass- it "kemEncryptEncapsulatedKeyLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"- encapsulatedKeyLength <- kemEncryptEncapsulatedKeyLength kem- pass- it "kemEncryptCreateSharedKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"- (sharedKey, encapsulatedKey) <- kemEncryptCreateSharedKey kem rng "salt" 256- pass- it "kemDecryptCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- kem <- kemDecryptCreate privKey "HKDF(SHA-256)"- pass- it "kemDecryptSharedKeyLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- kem <- kemDecryptCreate privKey "HKDF(SHA-256)"- sharedKeyLen <- kemDecryptSharedKeyLength kem 256- pass- it "kemDecryptSharedKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- encryptCtx <- kemEncryptCreate pubKey "HKDF(SHA-256)"- (sharedKey, encapsulatedKey) <- kemEncryptCreateSharedKey encryptCtx rng "salt" 256- decryptCtx <- kemDecryptCreate privKey "HKDF(SHA-256)"- decryptedSharedKey <- kemDecryptSharedKey decryptCtx "salt" encapsulatedKey 256- sharedKey `shouldBe` decryptedSharedKey- pass
− test/Botan/Low/PubKey/RSASpec.hs
@@ -1,43 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.RSA--rsaSizes = [ "2048" ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp--main :: IO ()-main = hspec $ testSuite rsaSizes chars $ \ rsaSize -> do- it "privKeyLoadRSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "RSA" rsaSize rng- p <- privKeyField privKey "p"- q <- privKeyField privKey "q"- e <- privKeyField privKey "e"- loadedKey <- privKeyLoadRSA p q e- pass- it "pubKeyLoadRSA" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "RSA" rsaSize rng- pubKey <- privKeyExportPubKey privKey- n <- pubKeyField pubKey "n"- e <- pubKeyField pubKey "e"- loadedKey <- pubKeyLoadRSA n e- pass
− test/Botan/Low/PubKey/SM2Spec.hs
@@ -1,71 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.SM2---- TODO: Consolidate-ecGroups :: [ByteString]-ecGroups =- [ "secp160k1"- , "secp160r1"- , "secp160r2"- , "secp192k1"- , "secp192r1"- , "secp224k1"- , "secp224r1"- , "secp256k1"- , "secp256r1"- , "secp384r1"- , "secp521r1"- , "brainpool160r1"- , "brainpool192r1"- , "brainpool224r1"- , "brainpool256r1"- , "brainpool320r1"- , "brainpool384r1"- , "brainpool512r1"- , "x962_p192v2"- , "x962_p192v3"- , "x962_p239v1"- , "x962_p239v2"- , "x962_p239v3"- , "gost_256A"- , "gost_512A"- , "frp256v1"- , "sm2p256v1"- ]---- TODO: Consolidate-privKeyField :: PrivKey -> ByteString -> IO MP-privKeyField privKey field = do- mp <- mpInit- privKeyGetField mp privKey field- return mp---- TODO: Consolidate-pubKeyField :: PubKey -> ByteString -> IO MP-pubKeyField pubKey field = do- mp <- mpInit- pubKeyGetField mp pubKey field- return mp--main :: IO ()-main = hspec $ testSuite ecGroups chars $ \ ecGroup -> do- it "privKeyLoadSM2" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "SM2" ecGroup rng- x <- privKeyField privKey "x"- loadedPrivKey <- privKeyLoadSM2 x ecGroup- pass- it "pubKeyLoadSM2" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "SM2" ecGroup rng- pubKey <- privKeyExportPubKey privKey- public_x <- pubKeyField pubKey "public_x"- public_y <- pubKeyField pubKey "public_y"- loadedPubKey <- pubKeyLoadSM2 public_x public_y ecGroup- pass
− test/Botan/Low/PubKey/SignSpec.hs
@@ -1,67 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.PubKey-import Botan.Low.PubKey.Sign-import Botan.Low.RNG--ecGroup :: ByteString-ecGroup = "secp256r1"--dlGroup :: ByteString-dlGroup = "modp/ietf/1024"--sha = "SHA-256"---- TODO: More exhaustive testing in botan--- pks :: [(PKName, PKParams, Padding)]-pks :: [(ByteString, ByteString, ByteString)]-pks =- [ ("RSA", "2048", "EMSA4(SHA-256)") -- NOTE: It is not yet known if any of the others suppport EMSA- , ("SM2", "sm2p256v1", "foo,SHA-256")- , ("DSA", dlGroup, sha)- , ("ECDSA", ecGroup, sha)- , ("ECKCDSA", ecGroup, sha)- , ("ECGDSA", ecGroup, sha)- , ("GOST-34.10", ecGroup, sha)- , ("Ed25519", "", "")- , ("XMSS", "XMSS-SHAKE_10_256", "")- -- TODO: Dilithium-x-y- , ("Dilithium", "", "")- -- TODO: SPHINCS+- ]--pkTestName :: (ByteString, ByteString, ByteString) -> String-pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding---- NOTE: Some algorithms do not support DER format encoding.-main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param, algo) -> do- it "signCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ctx <- signCreate privKey algo StandardFormatSignature- -- ctxDER <- signCreate privKey algo DERFormatSignature- pass- it "signOutputLength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ctx <- signCreate privKey algo StandardFormatSignature- sigLen <- signOutputLength ctx- -- ctxDER <- signCreate privKey algo DERFormatSignature- -- _ <- signOutputLength ctxDER- pass- it "signUpdate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ctx <- signCreate privKey algo StandardFormatSignature- signUpdate ctx "Fee fi fo fum!"- pass- it "signFinish" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- ctx <- signCreate privKey algo StandardFormatSignature- signUpdate ctx "Fee fi fo fum!"- sig <- signFinish ctx rng- pass
− test/Botan/Low/PubKey/VerifySpec.hs
@@ -1,79 +0,0 @@-module Main where--import Test.Prelude--import qualified Data.ByteString as ByteString--import Botan.Low.PubKey-import Botan.Low.PubKey.Sign-import Botan.Low.PubKey.Verify-import Botan.Low.RNG---- TODO: Consolidate test values with SignSpec--ecGroup :: ByteString-ecGroup = "secp256r1"--dlGroup :: ByteString-dlGroup = "modp/ietf/1024"--sha = "SHA-256"--pks :: [(ByteString, ByteString, ByteString)]-pks =- [ ("RSA", "2048", "EMSA4(SHA-256)")- , ("SM2", "sm2p256v1", "foo,SHA-256")- , ("DSA", dlGroup, sha)- , ("ECDSA", ecGroup, sha)- , ("ECKCDSA", ecGroup, sha)- , ("ECGDSA", ecGroup, sha)- , ("GOST-34.10", ecGroup, sha)- , ("Ed25519", "", "")- , ("XMSS", "XMSS-SHAKE_10_256", "")- -- TODO: Dilithium-x-y- , ("Dilithium", "", "")- -- TODO: SPHINCS+- ]--pkTestName :: (ByteString, ByteString, ByteString) -> String-pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding---- SEE: signFinish for more details--- NOTE: Verified signatures pass for all algorithms in standard format--- Fails for only 4 in DER format via BadParameterException, but for important algs:--- RSA, Ed25519, XMSS, Dilithium--- The context and signatures are created without throwing any errors,--- compared to earlier which failed to even create a context, but--- it is possible that I need to figure out correct parameters and I doubt--- it is coincidence that the EMSA4 / empty params are the ones that are failing.--- Yet, it works for standard format.--main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param, algo) -> do- it "verifyCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- verifyCreate pubKey algo StandardFormatSignature- pass- it "verifyUpdate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- ctx <- verifyCreate pubKey algo StandardFormatSignature- verifyUpdate ctx "Fee fi fo fum!"- pass- it "verifyFinish" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- signCtx <- signCreate privKey algo StandardFormatSignature- signUpdate signCtx "Fee fi fo fum!"- sig <- signFinish signCtx rng- -- print $ "Siglen is: " <> show (ByteString.length sig)- print sig- pubKey <- privKeyExportPubKey privKey- verifyCtx <- verifyCreate pubKey algo StandardFormatSignature- verifyUpdate verifyCtx "Fee fi fo fum!"- verified <- verifyFinish verifyCtx sig- verified `shouldBe` True- pass
− test/Botan/Low/PubKey/X25519Spec.hs
@@ -1,34 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.PubKey.X25519--main :: IO ()-main = hspec $ do- it "privKeyX25519GetPrivKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Curve25519" "" rng- exportedPrivKey <- privKeyX25519GetPrivKey privKey- pass- it "pubKeyX25519GetPubKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Curve25519" "" rng- pubKey <- privKeyExportPubKey privKey- exportedPubKey <- pubKeyX25519GetPubKey pubKey- pass- it "privKeyLoadX25519" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Curve25519" "" rng- exportedPrivKey <- privKeyX25519GetPrivKey privKey- loadedPrivKey <- privKeyLoadX25519 exportedPrivKey- pass- it "pubKeyLoadX25519" $ do- rng <- rngInit "system"- privKey <- privKeyCreate "Curve25519" "" rng- pubKey <- privKeyExportPubKey privKey- exportedPubKey <- pubKeyX25519GetPubKey pubKey- loadedPubKey <- pubKeyLoadX25519 exportedPubKey- pass
− test/Botan/Low/PubKeySpec.hs
@@ -1,188 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.MPI-import Botan.Low.RNG-import Botan.Low.PubKey--ecGroup :: ByteString-ecGroup = "secp256r1"--dlGroup :: ByteString-dlGroup = "modp/ietf/1024"---- TODO: More exhaustive testing in botan--- pks :: [(PKName, PKParams)]-pks :: [(ByteString, ByteString)]-pks =- [ ("RSA", "2048")- , ("SM2", ecGroup)- , ("ElGamal", dlGroup)- , ("DSA", dlGroup)- , ("ECDSA", ecGroup)- , ("ECKCDSA", ecGroup)- , ("ECGDSA", ecGroup)- , ("GOST-34.10", ecGroup)- , ("Ed25519", "")- , ("XMSS", "XMSS-SHAKE_10_256") -- NOTE: XMSS is noticeably slower than other keys and- -- the _16 and _20 variants are exponentially slower- , ("DH", dlGroup)- , ("ECDH", ecGroup)- , ("Curve25519", "")- -- TODO: Dilithium-x-y- , ("Dilithium", "")- -- TODO: Kyber-x-y- , ("Kyber", "")- -- TODO: SPHINCS+- -- https://github.com/randombit/botan/blob/a303f4af1504e7ac349dd798190924ea08ead9b7/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp#L155- -- , ("SPHINCS+", "sha2-128s-r3.1") -- Not working- , ("McEliece", "")- ]--pkTestName :: (ByteString, ByteString) -> String-pkTestName (pk, param) = chars $ pk <> " " <> param---- NOTE: Known fields are: n, e, p, q, g, a, b--- ECGroup PrivKey: x plus all in PubKey--- EGGroup PubKey: public_x, public_y, base_x, base_y, p, a, b, cofactor, order--- DLGroup PubKey: p, q, g, y (not all groups have all four)--- DLGroup PrivKey: p, q, g, x, y--- RSA PubKey: p, q, d, c, d1, d2--- RSA PrivKey: n, e, d, p, q, d1, d2, c--ecGroupPrivFields = [ "x" , "public_x", "public_y", "base_x", "base_y", "p", "a", "b", "cofactor", "order" ]-ecGroupPubFields = [ "public_x", "public_y", "base_x", "base_y", "p", "a", "b", "cofactor", "order" ]--dlGroupPrivFields = [ "p", "q", "g", "x", "y" ]-dlGroupPubFields = [ "p", "q", "g", "y" ]--privKeyFields "RSA" = [ "p", "q", "d", "n", "e"] -- TODO: Check-privKeyFields "SM2" = ecGroupPrivFields-privKeyFields "ElGamal" = dlGroupPrivFields-privKeyFields "DSA" = dlGroupPrivFields-privKeyFields "ECDSA" = ecGroupPrivFields-privKeyFields "ECKCDSA" = ecGroupPrivFields-privKeyFields "ECGDSA" = ecGroupPrivFields-privKeyFields "GOST-34.10" = ecGroupPrivFields-privKeyFields "Ed25519" = [] -- TODO-privKeyFields "XMSS" = [] -- TODO-privKeyFields "DH" = dlGroupPrivFields-privKeyFields "ECDH" = ecGroupPrivFields-privKeyFields "Curve25519" = [] -- TODO-privKeyFields "Dilithium" = [] -- TODO-privKeyFields "Kyber" = [] -- TODO-privKeyFields "McEliece" = [] -- TODO--pubKeyFields "RSA" = [ "n", "e" ]-pubKeyFields "SM2" = ecGroupPubFields-pubKeyFields "ElGamal" = dlGroupPubFields-pubKeyFields "DSA" = dlGroupPubFields-pubKeyFields "ECDSA" = ecGroupPubFields-pubKeyFields "ECKCDSA" = ecGroupPubFields-pubKeyFields "ECGDSA" = ecGroupPubFields-pubKeyFields "GOST-34.10" = ecGroupPubFields-pubKeyFields "Ed25519" = [] -- TODO-pubKeyFields "XMSS" = [] -- TODO-pubKeyFields "DH" = dlGroupPubFields-pubKeyFields "ECDH" = ecGroupPubFields-pubKeyFields "Curve25519" = [] -- TODO-pubKeyFields "Dilithium" = [] -- TODO-pubKeyFields "Kyber" = [] -- TODO-pubKeyFields "McEliece" = [] -- TODO---- NOTE: These tests are going to be very slow if we create new keys every time-main :: IO ()-main = hspec $ testSuite pks pkTestName $ \ (pk, param) -> do- it "privKeyCreate" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pass- it "privKeyCheckKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- privKeyCheckKey privKey rng CheckKeyNormalTests- -- privKeyCheckKey privKey rng CheckKeyExpensiveTests- pass- it "privKeyExport" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- privKeyDER <- privKeyExport privKey PrivKeyExportDER- privKeyPEM <- privKeyExport privKey PrivKeyExportPEM- pass- it "privKeyLoad" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- privKeyDER <- privKeyExport privKey PrivKeyExportDER- privKeyPEM <- privKeyExport privKey PrivKeyExportPEM- privKeyLoad privKeyDER ""- privKeyLoad privKeyPEM ""- pass- it "privKeyAlgoName" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- privKeyAlgoName privKey- pass- it "privKeyExportPubKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pass- it "pubKeyCheckKey" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyCheckKey pubKey rng CheckKeyNormalTests- -- pubKeyCheckKey pubKey rng CheckKeyExpensiveTests- pass- it "pubKeyExport" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyDER <- pubKeyExport pubKey PrivKeyExportDER- pubKeyPEM <- pubKeyExport pubKey PrivKeyExportPEM- pass- it "pubKeyLoad" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyDER <- pubKeyExport pubKey PrivKeyExportDER- pubKeyPEM <- pubKeyExport pubKey PrivKeyExportPEM- pubKeyLoad pubKeyDER- pubKeyLoad pubKeyPEM- pass- it "pubKeyAlgoName" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyAlgoName pubKey- pass- it "pubKeyEstimatedStrength" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyEstimatedStrength pubKey- pass- it "pubKeyFingerprint" $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- pubKeyFingerprint pubKey "SHA-256"- pass- describe "privKeyGetField" $ do- forM_ (privKeyFields pk) $ \ field -> do- it (chars field) $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- mp <- mpInit- privKeyGetField mp privKey field- pass- describe "pubKeyGetField" $ do- forM_ (pubKeyFields pk) $ \ field -> do- it (chars field) $ do- rng <- rngInit "system"- privKey <- privKeyCreate pk param rng- pubKey <- privKeyExportPubKey privKey- mp <- mpInit- pubKeyGetField mp pubKey field- pass
− test/Botan/Low/PwdHashSpec.hs
@@ -1,61 +0,0 @@-module Main where--import Data.ByteString (isPrefixOf)--import Botan.Low.Hash-import Botan.Low.MAC-import Botan.Low.PwdHash--import Test.Prelude---- NOTE: Needs more exhaustive tests, and to validate parameter order for--- Scrypt and the Argons. We've got tests that pass, but that doesn't--- mean that they're being used properly. Need to investigate C++ source.---- NOTE: Values generated by "pwdhashTimed pbkdf 200 64 passphrase salt"-pbkdfs :: [(PBKDFName, Int, Int, Int)]-pbkdfs =- [ ("PBKDF2(HMAC(SHA-512))",138000,0,0)- -- NOTE: These results indicate that the parameter result order is inconsistent- -- for pwdhashTimed compared to pwdhash- -- Eg, Scrypt should be n, r, p but n=8192 is clearly last- -- Same for the argons- -- This causes both tests to fail (original generated values)- -- , ("Scrypt",1,81,8192)- -- , ("Argon2d",1,1,262144)- -- , ("Argon2i",1,1,262144)- -- , ("Argon2id",1,1,262144)- -- Note that this still causes four pwdhashTimed tests to still fail because- -- the pwdhashTimed function itself needs to be fixed for these algorithms- -- Fixed values, assuming only x/z are flipped a la (x,y,z) -> (z,y,x)- -- But- , ("Scrypt",8192,81,1)- , ("Argon2d",262144,1,1)- , ("Argon2i",262144,1,1)- , ("Argon2id",262144,1,1)- , ("Bcrypt-PBKDF",26,0,0)- , ("OpenPGP-S2K(SHA-512)",65011712,0,0)- ]--passphrase :: ByteString-passphrase = "Fee fi fo fum!"--salt :: ByteString-salt = "salt"--main :: IO ()-main = hspec $ testSuite pbkdfs (\(n,_,_,_) -> chars n) $ \ (pbkdf, i, j, k) -> do- it "pwdhash" $ do- _ <- pwdhash pbkdf i j k 64 passphrase salt- pass- it "pwdhashTimed" $ do- timed@(i',j',k',pwd) <- pwdhashTimed pbkdf 200 64 passphrase salt- -- NOTE: Fails parity for Scrypt and the Argons due to parameter order- -- pwd' <- pwdhash pbkdf i' j' k' 64 passphrase salt- --NOTE: Scrypt still fails parity and flipping j and k doesn't matter.- pwd' <- case pbkdf of- "Scrypt" -> pwdhash pbkdf j' k' i' 64 passphrase salt- _ | "Argon" `isPrefixOf` pbkdf -> pwdhash pbkdf k' j' i' 64 passphrase salt- _ -> pwdhash pbkdf i' j' k' 64 passphrase salt- pwd `shouldBe` pwd'- pass
− test/Botan/Low/RNGSpec.hs
@@ -1,42 +0,0 @@-module Main where--import Test.Prelude--import qualified Data.ByteString as ByteString--import Botan.Low.RNG--rngs :: [RNGType]-rngs =- [ SystemRNG- , UserRNG- , UserThreadsafeRNG- , RDRandRNG -- NOTES: Not available on all processors- ]--main :: IO ()-main = hspec $ do- it "systemRNGGet" $ do- bs <- systemRNGGet 8- ByteString.length bs `shouldBe` 8- testSuite rngs chars $ \ rng -> do- it "rngInit" $ do- ctx <- rngInit rng- pass- it "rngGet" $ do- ctx <- rngInit rng- bs <- rngGet ctx 8- pass- it "rngReseed" $ do- ctx <- rngInit rng- rngReseed ctx 64- pass- it "rngReseedFromRNGCtx" $ do- ctx <- rngInit rng- source <- rngInit rng- rngReseedFromRNG ctx source 64- pass- it "rngAddEntropy" $ do- ctx <- rngInit rng- rngAddEntropy ctx "Fee fi fo fum!"- pass
− test/Botan/Low/SRP6Spec.hs
@@ -1,71 +0,0 @@-{-# LANGUAGE CPP #-}--#if defined(MIN_VERSION_GLASGOW_HASKELL)-#if MIN_VERSION_GLASGOW_HASKELL(9,8,0,0)-{-# OPTIONS_GHC -Wwarn=x-partial #-}-#endif-#endif--module Main where--import Test.Prelude--import Botan.Low.SRP6-import Botan.Low.RNG-import Botan.Low.PubKey-import Botan.Low.Hash--username :: ByteString-username = "username"--password :: ByteString-password = "password"--salt :: ByteString-salt = "salt"---- NOTE: Consolidate with DLGroup-groupIds :: [DLGroupName]-groupIds =- [ "ffdhe/ietf/2048"- , "ffdhe/ietf/3072"- , "ffdhe/ietf/4096"- , "ffdhe/ietf/6144"- , "ffdhe/ietf/8192"- , "modp/ietf/1024"- , "modp/ietf/1536"- , "modp/ietf/2048"- , "modp/ietf/3072"- , "modp/ietf/4096"- , "modp/ietf/6144"- , "modp/ietf/8192"- , "modp/srp/1024"- , "modp/srp/1536"- , "modp/srp/2048"- , "modp/srp/3072"- , "modp/srp/4096"- , "modp/srp/6144"- , "modp/srp/8192"- , "dsa/jce/1024"- , "dsa/botan/2048"- , "dsa/botan/3072"- ]--groupId :: DLGroupName-groupId = head groupIds---- TODO: Test which hashes work-hashId :: HashName-hashId = "SHA-256"--main :: IO ()-main = hspec $ testSuite groupIds chars $ \ groupId -> do- it "can negotiate a shared secret" $ do- rng <- rngInit "system"- verifier <- srp6GenerateVerifier username password salt groupId hashId- ctx <- srp6ServerSessionInit- b <- srp6ServerSessionStep1 ctx verifier groupId hashId rng- (a,sharedSecret) <- srp6ClientAgree username password groupId hashId salt b rng- sharedSecret' <- srp6ServerSessionStep2 ctx a- sharedSecret `shouldBe` sharedSecret'- pass
− test/Botan/Low/TOTPSpec.hs
@@ -1,31 +0,0 @@-module Main where--import Test.Prelude-import Botan.Low.Hash (HashName(..))--import Botan.Low.TOTP--key :: ByteString-key = "Fee fi fo fum"--timestep :: TOTPTimestep-timestep = 60--timestamp :: TOTPTimestamp-timestamp = 1698109812---- TODO: More thorough testing-main :: IO ()-main = hspec $ testSuite totpHashes chars $ \ h -> do- it "totpInit" $ do- ctx <- totpInit key h 6 timestep- pass- it "totpGenerate" $ do- ctx <- totpInit key h 6 timestep- code <- totpGenerate ctx timestamp- pass- it "totpCheck" $ do- ctx <- totpInit key h 6 timestep- code <- totpGenerate ctx timestamp- success <- totpCheck ctx code timestamp 0- success `shouldBe` True
− test/Botan/Low/UtilitySpec.hs
@@ -1,41 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.Utility--import qualified Data.ByteString as ByteString--message = "Fee fi fo fum!"--hexMessage = "46656520666920666F2066756D21"--base64Message = "RmVlIGZpIGZvIGZ1bSE="---- hexUpperMessage--main :: IO ()-main = hspec $ do- it "constantTimeCompare" $ do- equal <- constantTimeCompare "compare me" "compare me" (ByteString.length "compare me")- equal `shouldBe` True- pass- -- TODO:- -- it "scrubMem" $ do- -- pass- it "hexEncode" $ do- hex <- hexEncode message HexUpperCase- hex `shouldBe` hexMessage- pass- it "hexDecode" $ do- msg <- hexDecode hexMessage- msg `shouldBe` message- pass- it "base64Encode" $ do- base64 <- base64Encode message- base64 `shouldBe` base64Message- pass- it "base64Decode" $ do- msg <- base64Decode base64Message- msg `shouldBe` message- pass
− test/Botan/Low/X509Spec.hs
@@ -1,171 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.X509--import Paths_botan_low---- NOTE: Taken from https://fm4dd.com/openssl/certexamples.shtm-testCert :: ByteString-testCert = "-----BEGIN CERTIFICATE-----\-\MIID2jCCA0MCAg39MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\-\A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\-\MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\-\YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\-\ODIyMDUyODAwWhcNMTcwODIxMDUyODAwWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\-\CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\-\ZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwvWITOLeyTbS1\-\Q/UacqeILIK16UHLvSymIlbbiT7mpD4SMwB343xpIlXN64fC0Y1ylT6LLeX4St7A\-\cJrGIV3AMmJcsDsNzgo577LqtNvnOkLH0GojisFEKQiREX6gOgq9tWSqwaENccTE\-\sAXuV6AQ1ST+G16s00iN92hjX9V/V66snRwTsJ/p4WRpLSdAj4272hiM19qIg9zr\-\h92e2rQy7E/UShW4gpOrhg2f6fcCBm+aXIga+qxaSLchcDUvPXrpIxTd/OWQ23Qh\-\vIEzkGbPlBA8J7Nw9KCyaxbYMBFb1i0lBjwKLjmcoihiI7PVthAOu/B71D2hKcFj\-\Kpfv4D1Uam/0VumKwhwuhZVNjLq1BR1FKRJ1CioLG4wCTr0LVgtvvUyhFrS+3PdU\-\R0T5HlAQWPMyQDHgCpbOHW0wc0hbuNeO/lS82LjieGNFxKmMBFF9lsN2zsA6Qw32\-\Xkb2/EFltXCtpuOwVztdk4MDrnaDXy9zMZuqFHpv5lWTbDVwDdyEQNclYlbAEbDe\-\vEQo/rAOZFl94Mu63rAgLiPeZN4IdS/48or5KaQaCOe0DuAb4GWNIQ42cYQ5TsEH\-\Wt+FIOAMSpf9hNPjDeu1uff40DOtsiyGeX9NViqKtttaHpvd7rb2zsasbcAGUl+f\-\NQJj4qImPSB9ThqZqPTukEcM/NtbeQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIAi\-\gU3My8kYYniDuKEXSJmbVB+K1upHxWDA8R6KMZGXfbe5BRd8s40cY6JBYL52Tgqd\-\l8z5Ek8dC4NNpfpcZc/teT1WqiO2wnpGHjgMDuDL1mxCZNL422jHpiPWkWp3AuDI\-\c7tL1QjbfAUHAQYwmHkWgPP+T2wAv0pOt36GgMCM\-\-----END CERTIFICATE-----\-\"---- NOTE: Taken from https://fm4dd.com/openssl/certexamples.shtm-testCertFile :: FilePath-testCertFile = "test-data/4096b-rsa-example-cert.pem"--{--Version: 1-Subject: C="JP",X520.State="Tokyo",O="Frank4DD",CN="www.example.com"-Issuer: C="JP",X520.State="Tokyo",X520.Locality="Chuo-ku",O="Frank4DD",OU="WebCert Support",CN="Frank4DD Web CA",PKCS9.EmailAddress="support@frank4dd.com"-Issued: 2012/08/22 05:28:00 UTC-Expires: 2017/08/21 05:28:00 UTC-Constraints:- None-Signature algorithm: RSA/EMSA3(SHA-1)-Serial number: 0DFD-Public Key [RSA-4096]-------BEGIN PUBLIC KEY------MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsL1iEzi3sk20tUP1GnKn-iCyCtelBy70spiJW24k+5qQ+EjMAd+N8aSJVzeuHwtGNcpU+iy3l+ErewHCaxiFd-wDJiXLA7Dc4KOe+y6rTb5zpCx9BqI4rBRCkIkRF+oDoKvbVkqsGhDXHExLAF7leg-ENUk/hterNNIjfdoY1/Vf1eurJ0cE7Cf6eFkaS0nQI+Nu9oYjNfaiIPc64fdntq0-MuxP1EoVuIKTq4YNn+n3AgZvmlyIGvqsWki3IXA1Lz166SMU3fzlkNt0IbyBM5Bm-z5QQPCezcPSgsmsW2DARW9YtJQY8Ci45nKIoYiOz1bYQDrvwe9Q9oSnBYyqX7+A9-VGpv9FbpisIcLoWVTYy6tQUdRSkSdQoqCxuMAk69C1YLb71MoRa0vtz3VEdE+R5Q-EFjzMkAx4AqWzh1tMHNIW7jXjv5UvNi44nhjRcSpjARRfZbDds7AOkMN9l5G9vxB-ZbVwrabjsFc7XZODA652g18vczGbqhR6b+ZVk2w1cA3chEDXJWJWwBGw3rxEKP6w-DmRZfeDLut6wIC4j3mTeCHUv+PKK+SmkGgjntA7gG+BljSEONnGEOU7BB1rfhSDg-DEqX/YTT4w3rtbn3+NAzrbIshnl/TVYqirbbWh6b3e629s7GrG3ABlJfnzUCY+Ki-Jj0gfU4amaj07pBHDPzbW3kCAwEAAQ==------END PUBLIC KEY--------}--testCertHostname :: ByteString-testCertHostname = "www.example.com"--testCertValidTimestamp = 1420092000--main :: IO ()-main = hspec $ do- it "x509CertLoad" $ do- cert <- x509CertLoad testCert- pass- it "x509CertLoadFile" $ do- cert <- x509CertLoadFile =<< getDataFileName testCertFile- pass- it "x509CertDup" $ do- cert <- x509CertLoad testCert- dup <- x509CertDup cert- -- TODO: parity check, eg: x509CertGetPublicKey cert == x509CertGetPublicKey dup- pass- it "x509CertGetTimeStarts" $ do- cert <- x509CertLoad testCert- ts <- x509CertGetTimeStarts cert- pass- it "x509CertGetTimeExpires" $ do- cert <- x509CertLoad testCert- te <- x509CertGetTimeExpires cert- pass- it "x509CertNotBefore" $ do- cert <- x509CertLoad testCert- nb <- x509CertNotBefore cert- pass- it "x509CertNotAfter" $ do- cert <- x509CertLoad testCert- na <- x509CertNotAfter cert- pass- it "x509CertGetPubKeyFingerprint" $ do- cert <- x509CertLoad testCert- fp <- x509CertGetPubKeyFingerprint cert "SHA-256" -- TODO: HashName- pass- it "x509CertGetSerialNumber" $ do- cert <- x509CertLoad testCert- sn <- x509CertGetSerialNumber cert- pass- it "x509CertGetAuthorityKeyId" $ do- cert <- x509CertLoad testCert- akid <- x509CertGetAuthorityKeyId cert- pass- it "x509CertGetSubjectKeyId" $ do- cert <- x509CertLoad testCert- sid <- x509CertGetSubjectKeyId cert- pass- it "x509CertGetPublicKeyBits" $ do- cert <- x509CertLoad testCert- pkbits <- x509CertGetPublicKeyBits cert- pass- it "x509CertGetPublicKey" $ do- cert <- x509CertLoad testCert- pk <- x509CertGetPublicKey cert- pass- it "x509CertGetIssuerDN" $ do- cert <- x509CertLoad testCert- _ <- x509CertGetIssuerDN cert "CN" 0 -- TODO: Distinguished names, etc- pass- it "x509CertGetSubjectDN" $ do- cert <- x509CertLoad testCert- _ <- x509CertGetSubjectDN cert "CN" 0 -- TODO: Distinguished names, etc- pass- it "x509CertToString" $ do- cert <- x509CertLoad testCert- pass- it "x509CertAllowedUsage" $ do- cert <- x509CertLoad testCert- allowed <- x509CertAllowedUsage cert NoConstraints- pass- it "x509CertHostnameMatch" $ do- cert <- x509CertLoad testCert- matched <- x509CertHostnameMatch cert testCertHostname- pass- it "x509CertVerify" $ do- cert <- x509CertLoad testCert- (success, status) <- x509CertVerify cert [] [] Nothing 0 testCertHostname testCertValidTimestamp- pass- it "x509CertValidationStatus" $ do- cert <- x509CertLoad testCert- _ <- x509CertValidationStatus 0- pending- -- NOTE: May need to generate a proper test cert with CA and CRL- it "x509CRLLoad" $ do- cert <- x509CertLoad testCert- -- x509CRLLoad cert- pending- it "x509CRLLoadFile" $ do- cert <- x509CertLoad testCert- -- x509CRLLoadFile cert- pending- it "x509IsRevoked" $ do- cert <- x509CertLoad testCert- -- x509IsRevoked cert- pending- it "x509CertVerifyWithCLR" $ do- cert <- x509CertLoad testCert- -- x509CertVerifyWithCLR cert- pending-
− test/Botan/Low/ZFECSpec.hs
@@ -1,27 +0,0 @@-module Main where--import Test.Prelude--import Botan.Low.ZFEC--k :: Int-k = 5--n :: Int-n = 7--message :: ByteString-message = "Fee fi fo fum! I smell the blood of an Englishman!"--main :: IO ()-main = hspec $ do- describe "zfecEncode" $ do- it "encodes a message into shares" $ do- shares <- zfecEncode k n message- length shares `shouldBe` n- describe "zfecDecode" $ do- it "recovers a message from enough shares" $ do- shares <- zfecEncode k n message- someShares <- take k <$> generate (shuffle shares)- recoveredMessage <- zfecDecode k n someShares- recoveredMessage `shouldBe` message
+ test/Main.hs view
@@ -0,0 +1,117 @@+module Main (main) where++import qualified Test.Botan.Low.Bcrypt+import qualified Test.Botan.Low.BlockCipher+import qualified Test.Botan.Low.Cipher+import qualified Test.Botan.Low.FPE+import qualified Test.Botan.Low.Hash+import qualified Test.Botan.Low.HOTP+import qualified Test.Botan.Low.KDF+import qualified Test.Botan.Low.KeyWrap+import qualified Test.Botan.Low.MAC+import qualified Test.Botan.Low.MPI+import qualified Test.Botan.Low.PubKey+import qualified Test.Botan.Low.PubKey.Decrypt+import qualified Test.Botan.Low.PubKey.DH+import qualified Test.Botan.Low.PubKey.DSA+import qualified Test.Botan.Low.PubKey.ECDH+import qualified Test.Botan.Low.PubKey.ECDSA+import qualified Test.Botan.Low.PubKey.Ed25519+import qualified Test.Botan.Low.PubKey.ElGamal+import qualified Test.Botan.Low.PubKey.Encrypt+import qualified Test.Botan.Low.PubKey.KeyAgreement+import qualified Test.Botan.Low.PubKey.KeyEncapsulation+import qualified Test.Botan.Low.PubKey.RSA+import qualified Test.Botan.Low.PubKey.Sign+import qualified Test.Botan.Low.PubKey.SM2+import qualified Test.Botan.Low.PubKey.Verify+import qualified Test.Botan.Low.PubKey.X25519+import qualified Test.Botan.Low.PwdHash+import qualified Test.Botan.Low.RNG+import qualified Test.Botan.Low.SRP6+import qualified Test.Botan.Low.SRP6.Example+import qualified Test.Botan.Low.TOTP+import qualified Test.Botan.Low.Utility+import qualified Test.Botan.Low.Version+import qualified Test.Botan.Low.X509+import qualified Test.Botan.Low.ZFEC+import Test.Tasty+import Test.Tasty.HUnit++main :: IO ()+main = tests >>= defaultMain++tests :: IO TestTree+tests = do+ bcryptTests <- Test.Botan.Low.Bcrypt.tests+ blockCipherTests <- Test.Botan.Low.BlockCipher.tests+ cipherTests <- Test.Botan.Low.Cipher.tests+ fpeTests <- Test.Botan.Low.FPE.tests+ hashTests <- Test.Botan.Low.Hash.tests+ hotpTests <- Test.Botan.Low.HOTP.tests+ kdfTests <- Test.Botan.Low.KDF.tests+ keyWrapTests <- Test.Botan.Low.KeyWrap.tests+ macTests <- Test.Botan.Low.MAC.tests+ mpiTests <- Test.Botan.Low.MPI.tests+ pubKeyTests <- Test.Botan.Low.PubKey.tests+ pubKeyDecryptTests <- Test.Botan.Low.PubKey.Decrypt.tests+ pubKeyDhTests <- Test.Botan.Low.PubKey.DH.tests+ pubKeyDsaTests <- Test.Botan.Low.PubKey.DSA.tests+ pubKeyEcdhTests <- Test.Botan.Low.PubKey.ECDH.tests+ pubKeyEcdsaTests <- Test.Botan.Low.PubKey.ECDSA.tests+ pubKeyEd25519Tests <- Test.Botan.Low.PubKey.Ed25519.tests+ pubKeyElGamalTests <- Test.Botan.Low.PubKey.ElGamal.tests+ pubKeyEncryptTests <- Test.Botan.Low.PubKey.Encrypt.tests+ pubKeyKeyAgreementTests <- Test.Botan.Low.PubKey.KeyAgreement.tests+ pubKeyKeyEncapsulationTests <- Test.Botan.Low.PubKey.KeyEncapsulation.tests+ pubKeyRsaTests <- Test.Botan.Low.PubKey.RSA.tests+ pubKeySignTests <- Test.Botan.Low.PubKey.Sign.tests+ pubKeySm2Tests <- Test.Botan.Low.PubKey.SM2.tests+ pubKeyVerifyTests <- Test.Botan.Low.PubKey.Verify.tests+ pubKeyX25519Tests <- Test.Botan.Low.PubKey.X25519.tests+ pwdHashTests <- Test.Botan.Low.PwdHash.tests+ rngTests <- Test.Botan.Low.RNG.tests+ srp6Tests <- Test.Botan.Low.SRP6.tests+ totpTests <- Test.Botan.Low.TOTP.tests+ utilityTests <- Test.Botan.Low.Utility.tests+ x509Tests <- Test.Botan.Low.X509.tests+ zfecTests <- Test.Botan.Low.ZFEC.tests+ pure $ testGroup "botan-low" [+ bcryptTests+ , blockCipherTests+ , cipherTests+ , fpeTests+ , hashTests+ , hotpTests+ , kdfTests+ , keyWrapTests+ , macTests+ , mpiTests+ , pubKeyTests+ , pubKeyDecryptTests+ , pubKeyDhTests+ , pubKeyDsaTests+ , pubKeyEcdhTests+ , pubKeyEcdsaTests+ , pubKeyEd25519Tests+ , pubKeyElGamalTests+ , pubKeyEncryptTests+ , pubKeyKeyAgreementTests+ , pubKeyKeyEncapsulationTests+ , pubKeyRsaTests+ , pubKeySignTests+ , pubKeySm2Tests+ , pubKeyVerifyTests+ , pubKeyX25519Tests+ , pwdHashTests+ , rngTests+ , srp6Tests+ , testGroup "Test.Botan.Low.SRP6.Example" [+ testCase "example" Test.Botan.Low.SRP6.Example.main+ ]+ , totpTests+ , utilityTests+ , Test.Botan.Low.Version.tests+ , x509Tests+ , zfecTests+ ]
+ test/Test/Botan/Low/Bcrypt.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.Bcrypt (tests) where++import Botan.Low.Bcrypt+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_bcrypt" spec_bcrypt+ pure $ testGroup "Test.Botan.Low.Bcrypt" [+ specs+ ]++password :: ByteString+password = "Fee fi fo fum!"++factor :: BcryptWorkFactor+factor = 12++spec_bcrypt :: Spec+spec_bcrypt = do+ describe "bcryptGenerate" $ do+ it "generates a bcrypt hash" $ do+ rng <- rngInit "user-threadsafe"+ _ <- bcryptGenerate password rng factor+ pass+ describe "bcryptIsValid" $ do+ it "validates a bcrypt hash" $ do+ rng <- rngInit "user-threadsafe"+ h <- bcryptGenerate password rng factor+ valid <- bcryptIsValid password h+ valid `shouldBe` True
+ test/Test/Botan/Low/BlockCipher.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.BlockCipher (+ tests+ ) where++import Botan.Low.BlockCipher+import Botan.Low.Hash+import Botan.Low.RNG+import qualified Data.ByteString as BS+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_blockCipher" spec_blockCipher+ pure $ testGroup "Test.Botan.Low.BlockCipher" [+ specs+ ]++{-------------------------------------------------------------------------------+ Specs+-------------------------------------------------------------------------------}++spec_blockCipher :: Spec+spec_blockCipher = testSuite allTestBlockCiphers chars $ \ bc -> do+ it "can initialize a block cipher context" $ do+ _ctx <- blockCipherInit bc+ pass+ it "has a name" $ do+ ctx <- blockCipherInit bc+ _name <- blockCipherName ctx+ -- name `shouldBe` bc -- Name expands to include default parameters - need to record+ pass+ it "has a key spec" $ do+ ctx <- blockCipherInit bc+ (_,_,_) <- blockCipherGetKeyspec ctx+ pass+ it "has a block size" $ do+ ctx <- blockCipherInit bc+ _ <- blockCipherBlockSize ctx+ pass+ it "can set the key" $ do+ ctx <- blockCipherInit bc+ (_,mx,_) <- blockCipherGetKeyspec ctx+ k <- systemRNGGet mx+ blockCipherSetKey ctx k+ pass+ it "can encipher a message" $ do+ ctx <- blockCipherInit bc+ (_,mx,_) <- blockCipherGetKeyspec ctx+ k <- systemRNGGet mx+ blockCipherSetKey ctx k+ bsz <- blockCipherBlockSize ctx+ msg <- systemRNGGet $ bsz * 10+ _encmsg <- blockCipherEncryptBlocks ctx msg+ pass+ it "can only encipher a message that is a multiple of the block size" $ do+ ctx <- blockCipherInit bc+ (_,mx,_) <- blockCipherGetKeyspec ctx+ k <- systemRNGGet mx+ blockCipherSetKey ctx k+ bsz <- blockCipherBlockSize ctx+ msg <- systemRNGGet $ bsz * 10+ let msgCorrupted = msg <> BS.pack [17]+ blockCipherEncryptBlocks ctx msgCorrupted+ `shouldThrow` anyErrorCall+ it "can decipher an enciphered message" $ do+ ctx <- blockCipherInit bc+ (_,mx,_) <- blockCipherGetKeyspec ctx+ k <- systemRNGGet mx+ blockCipherSetKey ctx k+ bsz <- blockCipherBlockSize ctx+ msg <- systemRNGGet $ bsz * 10+ encmsg <- blockCipherEncryptBlocks ctx msg+ decmsg <- blockCipherDecryptBlocks ctx encmsg+ decmsg `shouldBe` msg+ pass+ it "can only decipher an enciphered message that is a multiple of the block size" $ do+ ctx <- blockCipherInit bc+ (_,mx,_) <- blockCipherGetKeyspec ctx+ k <- systemRNGGet mx+ blockCipherSetKey ctx k+ bsz <- blockCipherBlockSize ctx+ msg <- systemRNGGet $ bsz * 10+ encmsg <- blockCipherEncryptBlocks ctx msg+ let encmsgCorrupted = BS.take (BS.length encmsg - 1) encmsg+ blockCipherDecryptBlocks ctx encmsgCorrupted+ `shouldThrow` anyErrorCall++allTestBlockCiphers :: [BlockCipherName]+allTestBlockCiphers = fmap adjust allBlockCiphers+ where+ adjust Cascade = cascade Serpent AES256+ adjust Lion = lion SHA1 "RC4" (Just 64)+ adjust name = name
+ test/Test/Botan/Low/Cipher.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-deprecations #-}++module Test.Botan.Low.Cipher (tests) where++import Botan.Bindings.Version (botan_version_major,+ botan_version_minor)+import Botan.Low.Cipher+import Botan.Low.RNG+import Control.Monad+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import System.IO.Unsafe (unsafePerformIO)+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_cipher" spec_cipher+ pure $ testGroup "Test.Botan.Low.Cipher" [+ specs+ ]++testModes :: [ByteString]+testModes = filter p (cipherModes ++ aeads)+ where+ p s+ -- TODO: also test "Lion" and "Cascade"+ | "Lion" `BS.isPrefixOf` s || "Cascade" `BS.isPrefixOf` s+ = False+ -- SIV and CCM have bugs on versions earlier than 3.5+ | unsafePerformIO botan_version_major == 3+ , unsafePerformIO botan_version_minor <= 4+ , "SIV" `BS.isSuffixOf` s || "CCM" `BS.isSuffixOf` s+ = False+ | otherwise+ = True++spec_cipher :: Spec+spec_cipher = testSuite testModes chars $ \ cipher -> do+ it "can initialize a cipher encryption context" $ do+ _ctx <- cipherInit cipher CipherEncrypt+ pass+ it "can initialize a cipher decryption context" $ do+ _ctx <- cipherInit cipher CipherDecrypt+ pass+ it "has a name" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _name <- cipherName ctx+ -- name `shouldBe` bc -- Name expands to include default parameters - need to record+ pass+ it "has a key spec" $ do+ ctx <- cipherInit cipher CipherEncrypt+ (_,_,_) <- cipherGetKeyspec ctx+ pass+ it "can set the key" $ do+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ pass+ it "has a valid default nonce length" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _nlen <- cipherGetDefaultNonceLength ctx+ pass+ it "can validate nonce length" $ do+ ctx <- cipherInit cipher CipherEncrypt+ nlen <- cipherGetDefaultNonceLength ctx+ defaultIsValid <- cipherValidNonceLength ctx nlen+ defaultIsValid `shouldBe` True+ -- NOTE: Some ciphers accept nonces of any length, eg SIV+ -- Others allow any length > 0, eg GCM+ -- Some have ranges+ -- Some have just 'if (length == 0) return false;' eg OCB+ -- Search C++ source for 'valid_nonce_length' to find them all+ -- zeroIsValid <- cipherValidNonceLength ctx 0+ -- zeroIsValid `shouldBe` False+ -- absurdlyLargeIsValid <- cipherValidNonceLength ctx maxBound+ -- absurdlyLargeIsValid `shouldBe` False+ pass+ it "can calculate output length" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _olen <- cipherOutputLength ctx 1024+ pass+ -- NOTE: This check should be ae / aead only+ it "has a tag length" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _ <- cipherGetTagLength ctx+ pass+ it "has an update graularity" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _ <- cipherGetUpdateGranularity ctx+ pass+ it "has an ideal update granularity" $ do+ ctx <- cipherInit cipher CipherEncrypt+ _ <- cipherGetIdealUpdateGranularity ctx+ pass+ if cipher `elem` aeads+ then it "can set the associated data" $ do+ ctx <- cipherInit cipher CipherEncrypt+ -- NOTE: Undocumented: A cipher must set the key before setting AD+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ -- END NOTE+ ad <- systemRNGGet 64+ cipherSetAssociatedData ctx ad+ pass+ else pass+ it "can reset all message state" $ do+ ctx <- cipherInit cipher CipherEncrypt+ cipherReset ctx+ pass+ it "can clear all internal state" $ do+ ctx <- cipherInit cipher CipherEncrypt+ cipherClear ctx+ pass+ it "can start processing a message" $ do+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ if cipher `elem` aeads+ then do+ ad <- systemRNGGet 64+ cipherSetAssociatedData ctx ad+ else pass+ n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx+ cipherStart ctx n+ pass+ -- TODO: More extensive testing in Botan; these are just binding sanity checks+ it "can one-shot / offline encipher a message" $ do+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ if cipher `elem` aeads+ then do+ ad <- systemRNGGet 64+ cipherSetAssociatedData ctx ad+ else pass+ n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx+ cipherStart ctx n+ g <- cipherGetIdealUpdateGranularity ctx+ msg <- systemRNGGet g+ _encmsg <- cipherEncrypt ctx msg+ pass+ it "can one-shot / offline decipher a message" $ do+ -- Same as prior test+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ ad <- systemRNGGet 64+ if cipher `elem` aeads+ then do+ cipherSetAssociatedData ctx ad+ else pass+ n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx+ cipherStart ctx n+ g <- cipherGetIdealUpdateGranularity ctx+ msg <- systemRNGGet g+ encmsg <- cipherEncrypt ctx msg+ -- Start actual test+ dctx <- cipherInit cipher CipherDecrypt+ cipherSetKey dctx k+ if cipher `elem` aeads+ then do+ cipherSetAssociatedData dctx ad+ else pass+ cipherStart dctx n+ decmsg <- cipherDecrypt dctx encmsg+ msg `shouldBe` decmsg+ pass+ it "can incrementally / online encipher a message" $ do+ -- Same as prior test+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ ad <- systemRNGGet 64+ if cipher `elem` aeads+ then do+ cipherSetAssociatedData ctx ad+ else pass+ n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx+ cipherStart ctx n+ g <- cipherGetIdealUpdateGranularity ctx+ msg <- systemRNGGet (8 * g)+ _encmsg <- cipherEncryptOnline ctx msg+ pass+ -- NOTE: Fails for SIV, CCM because they are offline-only algorithms+ -- This is expected, but not reflected in the tests yet+ it "can incrementally / online decipher a message" $ do+ ctx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec ctx+ k <- systemRNGGet mx+ cipherSetKey ctx k+ ad <- systemRNGGet 64+ if cipher `elem` aeads+ then do+ cipherSetAssociatedData ctx ad+ else pass+ n <- systemRNGGet =<< cipherGetDefaultNonceLength ctx+ cipherStart ctx n+ g <- cipherGetIdealUpdateGranularity ctx+ msg <- systemRNGGet (8 * g)+ encmsg <- cipherEncryptOnline ctx msg+ -- Start actual test+ dctx <- cipherInit cipher CipherDecrypt+ cipherSetKey dctx k+ if cipher `elem` aeads+ then do+ cipherSetAssociatedData dctx ad+ else pass+ cipherStart dctx n+ decmsg <- cipherDecryptOnline dctx encmsg+ msg `shouldBe` decmsg+ pass+ -- NOTE: Fails for SIV, CCM because they are offline-only algorithms+ -- This is expected, but not reflected in the tests yet+ it "has parity between online and offline" $ do+ onlinectx <- cipherInit cipher CipherEncrypt+ offlinectx <- cipherInit cipher CipherEncrypt+ (_,mx,_) <- cipherGetKeyspec onlinectx+ k <- systemRNGGet mx+ cipherSetKey onlinectx k+ cipherSetKey offlinectx k+ ad <- systemRNGGet 64+ when (cipher `elem` aeads) $ do+ cipherSetAssociatedData onlinectx ad+ cipherSetAssociatedData offlinectx ad+ n <- systemRNGGet =<< cipherGetDefaultNonceLength onlinectx+ cipherStart onlinectx n+ cipherStart offlinectx n+ g <- cipherGetIdealUpdateGranularity onlinectx+ msg <- systemRNGGet (8 * g)+ onlinemsg <- cipherEncryptOnline onlinectx msg+ offlinemsg <- cipherEncrypt offlinectx msg+ onlinemsg `shouldBe` offlinemsg+ pass
+ test/Test/Botan/Low/FPE.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.FPE (tests) where++import Botan.Low.FPE+import Botan.Low.MPI+import Botan.Low.RNG+import Control.Monad+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_fpe" spec_fpe+ pure $ testGroup "Test.Botan.Low.FPE" [+ specs+ ]++-- NOTE: FPE operations encrypt/decrypt integers less than n+nStr :: ByteString+nStr = "1000000000000"++key :: ByteString+key = "0000DEADBEEF0000"++rounds :: Int+rounds = 3++newRng :: IO RNG+newRng = rngInit "system"++newX :: MP -> IO MP+newX n = do+ x <- mpInit+ z <- mpInit+ mpSetFromInt z 0+ -- n <- mpInit+ -- mpSetFromStr n nStr+ rng <- newRng+ mpRandRange x rng z n+ return x++newFE1Ctx :: IO (MP, FPE)+newFE1Ctx = do+ n <- mpInit+ mpSetFromStr n nStr+ ctx <- fpeInitFE1 n key rounds FPENone+ return (n,ctx)++newFE1CtxCompatMode :: IO (MP, FPE)+newFE1CtxCompatMode = do+ n <- mpInit+ mpSetFromStr n nStr+ ctx <- fpeInitFE1 n key rounds FPEFE1CompatMode+ return (n,ctx)++spec_fpe :: Spec+spec_fpe = do+ describe "fpeInitFE1" $ do+ it "initializes an FE1 FPE context" $ do+ void newFE1Ctx+ pass+ it "initializes an FE1 FPE context in compat mode" $ (do+ void newFE1CtxCompatMode+ pass+ ) -- `shouldThrow` anyBotanException+ describe "fpeEncrypt" $ do+ it "performs format-preserving encryption" $ do+ (n,ctx) <- newFE1Ctx+ x <- newX n+ y <- mpCopy x+ fpeEncrypt ctx y ""+ isSame <- mpEqual x y+ isSame `shouldBe` False+ pass+ it "performs format-preserving encryption in compat mode" $ do+ (n,ctx) <- newFE1CtxCompatMode+ x <- newX n+ y <- mpCopy x+ fpeEncrypt ctx y ""+ isSame <- mpEqual x y+ isSame `shouldBe` False+ pass+ describe "fpeDecrypt" $ do+ it "performs format-preserving decryption" $ do+ (n,ctx) <- newFE1Ctx+ x <- newX n+ y <- mpCopy x+ fpeEncrypt ctx y ""+ z <- mpCopy y+ fpeDecrypt ctx z ""+ isSame <- mpEqual x z+ isSame `shouldBe` True+ pass+ it "performs format-preserving decryption in compat mode" $ do+ (n,ctx) <- newFE1CtxCompatMode+ x <- newX n+ y <- mpCopy x+ fpeEncrypt ctx y ""+ z <- mpCopy y+ fpeDecrypt ctx z ""+ isSame <- mpEqual x z+ isSame `shouldBe` True+ pass
+ test/Test/Botan/Low/HOTP.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.HOTP (tests) where++import Botan.Low.HOTP+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_HOTP" spec_HOTP+ pure $ testGroup "Test.Botan.Low.HOTP" [+ specs+ ]++key :: ByteString+key = "Fee fi fo fum"++counter :: HOTPCounter+counter = 12345++spec_HOTP :: Spec+spec_HOTP = testSuite hotpHashes chars $ \ h -> do+ it "hotpInit" $ do+ _ctx <- hotpInit key h 6+ pass+ it "hotpGenerate" $ do+ ctx <- hotpInit key h 6+ _code <- hotpGenerate ctx counter+ pass+ it "hotpCheck" $ do+ ctx <- hotpInit key h 6+ code <- hotpGenerate ctx counter+ (success,_next) <- hotpCheck ctx code counter 0+ success `shouldBe` True
+ test/Test/Botan/Low/Hash.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.Hash (tests) where++import Botan.Low.Hash+import Control.Monad+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_hash" spec_hash+ pure $ testGroup "Test.Botan.Low.Hash" [+ specs+ ]++message :: ByteString+message = "Fee fi fo fum! I smell the blood of an Englishman!"++spec_hash :: Spec+spec_hash = testSuite allHashes chars $ \ h -> do+ it "can initialize a hash context" $ do+ _ctx <- hashInit h+ pass+ it "has a name" $ do+ ctx <- hashInit h+ _name <- hashName ctx+ pass+ it "has an output length" $ do+ ctx <- hashInit h+ _olen <- hashOutputLength ctx+ pass+ it "can copy the internal state" $ do+ ctx <- hashInit h+ -- TODO: Populate with state and actually prove+ _ctx' <- hashCopyState ctx+ pass+ it "can clear all internal state" $ do+ ctx <- hashInit h+ -- TODO: Populate with state and actually prove+ hashClear ctx+ pass+ it "can update the internal state with a single message block" $ do+ ctx <- hashInit h+ hashUpdate ctx message+ pass+ it "can update the internal state with multiple message blocks" $ do+ ctx <- hashInit h+ forM_ (splitBlocks 4 message) $ hashUpdate ctx+ pass+ it "can finalize a digest" $ do+ ctx <- hashInit h+ hashUpdate ctx message+ _d <- hashFinal ctx+ pass
+ test/Test/Botan/Low/KDF.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.KDF (tests) where++import Botan.Low.KDF+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_kdf" spec_kdf+ pure $ testGroup "Test.Botan.Low.KDF" [+ specs+ ]+++-- NOTE: Many of these tests fail because different kdfs / macs / hashes have different requirements+-- such as input or output lengths.+-- Failing tests:+-- - Checksums: Adler32, CRC24, CRC32, BadParameterException for almost everything+-- - Probably because they are not cryptographic hashes+-- - All fail for HKDF, SP800-108-*(HMAC(hash)), SP800-56A(hash), SP800-56A(HMAC(hash)), SP800-56C(hash), SP800-56C(HMAC(hash))+-- - CRC24 fails for KDF1, probably because we asked for 4 bits+-- - HKDF-Extract - BadParameterException for unknown reasons+-- - X9.42-PRF: About half fail with NotImplementedException+-- - SP800-108-*: Requires HMAC wrapping, may support other MACs?+-- - SP800-56A / SP800-56C: Allow HMAC wrapping, may support other MACs?+-- Many do accept HMAC(hash) as an argument instead of just hash+-- It is not yet known which if any MACs work aside from HMAC (eg whether anything with CMAC, GMAC does) due to non-exhaustive+-- enumeration of MACs in their own tests.+-- SP800 KDFs fail with InvalidKeyLengthException for X9.19-MAC, Poly1305, SipHash(2,4)+-- except for SP800-56A which fails with NotImplementedException+-- TODO: Exhaustive algorithm testing (see Botan.KDF notes)++-- NOTE: Some kdfs (HKDF-Extract) do not support label input arguments+spec_kdf :: Spec+spec_kdf = testSuite kdfs chars $ \ algo -> do+ describe "kdf" $ do+ it "can derive a key" $ do+ _key <- kdf algo 3 "secret" "salt" "" -- "label"+ pass
+ test/Test/Botan/Low/KeyWrap.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.KeyWrap (tests) where++import Botan.Low.BlockCipher+import Botan.Low.KeyWrap+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_keywrap" spec_keywrap+ pure $ testGroup "Test.Botan.Low.KeyWrap" [+ specs+ ]+kwKey :: ByteString+kwKey = "This key *must* be a multiple of 8 bytes"++kwpKey :: ByteString+kwpKey = "Fee fi fo fum! I smell the blood of an Englishman!"++spec_keywrap :: Spec+spec_keywrap = testSuite blockCipher128s chars $ \ algo -> do+ it "nistKeyWrapEncode" $ do+ (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo+ kek <- systemRNGGet keklen+ _wrappedKey <- nistKeyWrapEncode algo 0 kwKey kek+ pass+ it "nistKeyWrapDecode" $ do+ (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo+ kek <- systemRNGGet keklen+ wrappedKey <- nistKeyWrapEncode algo 0 kwKey kek+ unwrappedKey <- nistKeyWrapDecode algo 0 wrappedKey kek+ unwrappedKey `shouldBe` kwKey+ pass+ it "nistKeyWrapEncode padded" $ do+ (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo+ kek <- systemRNGGet keklen+ _wrappedKey <- nistKeyWrapEncode algo 1 kwpKey kek+ pass+ it "nistKeyWrapDecode padded" $ do+ (_,keklen,_) <- blockCipherGetKeyspec =<< blockCipherInit algo+ kek <- systemRNGGet keklen+ wrappedKey <- nistKeyWrapEncode algo 1 kwpKey kek+ unwrappedKey <- nistKeyWrapDecode algo 1 wrappedKey kek+ unwrappedKey `shouldBe` kwpKey+ pass
+ test/Test/Botan/Low/MAC.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.MAC (tests) where++import Botan.Low.MAC+import Botan.Low.RNG+import Control.Monad+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_mac" spec_mac+ pure $ testGroup "Test.Botan.Low.MAC" [+ specs+ ]++-- NOTE: Full testing of all algorithms will require botan ADT types instead+-- of names, to be able to query for acceptable key and nonce parameters++-- nonceMACs :: [(MACName, Maybe Int)]+-- nonceMACs = concat+-- [ [ "CMAC(" <> bc <> ")" | bc <- allBlockCiphers ]+-- , [ "GMAC(" <> bc <> ")" | bc <- allBlockCiphers ]+-- , [ "CBC-MAC(" <> bc <> ")" | bc <- allBlockCiphers ]+-- ]++-- macs :: [(MACName, Maybe Int)]+-- macs = concat+-- [ nonceMACs+-- , [ "HMAC(" <> h <> ")" | h <- hashes ]+-- , [ "Poly1305", "SipHash(2,4)", "X9.19-MAC" ]+-- ]++macsAndNonceSzs :: [(MACName, Int)]+macsAndNonceSzs =+ [ ("CMAC(AES-256)", 0)+ , ("GMAC(AES-256)", 16)+ -- , ("CBC-MAC(AES-256)", 16) -- Not supported+ , ("HMAC(SHA-3)", 0)+ , ("Poly1305", 0)+ , ("SipHash(2,4)", 0)+ , ("X9.19-MAC", 0)+ ]++message :: ByteString+message = "Fee fi fo fum! I smell the blood of an Englishman!"++spec_mac :: Spec+spec_mac = testSuite macsAndNonceSzs (chars . fst) $ \ (h,nonceSz) -> do+ it "can initialize a mac context" $ do+ _ctx <- macInit h+ pass+ it "has a name" $ do+ ctx <- macInit h+ _name <- macName ctx+ pass+ it "has an output length" $ do+ ctx <- macInit h+ _olen <- macOutputLength ctx+ pass+ -- it "can copy the internal state" $ do+ -- ctx <- macInit h+ -- -- TODO: Populate with state and actually prove+ -- ctx' <- macCopyState ctx+ -- pass+ it "has a key spec" $ do+ ctx <- macInit h+ (_,_,_) <- macGetKeyspec ctx+ pass+ it "can set the key" $ do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ pass+ -- it "has a nonce spec" $ do+ it "can set the nonce" $ do+ if nonceSz > 0+ then do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ n <- systemRNGGet nonceSz+ macSetNonce ctx n+ else pass+ it "can clear all internal state" $ do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ if nonceSz > 0+ then do+ n <- systemRNGGet nonceSz+ macSetNonce ctx n+ else pass+ -- TODO: Populate with state and actually prove+ macClear ctx+ pass+ it "can update the internal state with a single message block" $ do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ if nonceSz > 0+ then do+ n <- systemRNGGet nonceSz+ macSetNonce ctx n+ else pass+ macUpdate ctx message+ pass+ it "can update the internal state with multiple message blocks" $ do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ if nonceSz > 0+ then do+ n <- systemRNGGet nonceSz+ macSetNonce ctx n+ else pass+ forM_ (splitBlocks 4 message) $ macUpdate ctx+ pass+ it "can finalize a digest" $ do+ ctx <- macInit h+ (_,mx,_) <- macGetKeyspec ctx+ k <- systemRNGGet mx+ macSetKey ctx k+ if nonceSz > 0+ then do+ n <- systemRNGGet nonceSz+ macSetNonce ctx n+ else pass+ macUpdate ctx message+ _d <- macFinal ctx+ pass
+ test/Test/Botan/Low/MPI.hs view
@@ -0,0 +1,394 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.MPI (tests) where++import Botan.Low.MPI+import Botan.Low.RNG+import Prelude hiding (mod)+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_mpi" spec_mpi+ pure $ testGroup "Test.Botan.Low.MPI" [+ specs+ ]++-- NOTE: These unit tests are mostly checking that the functions are bound correctly.+-- These are kind of crappy and repetative, but that also exposes what needs to be done+-- in the higher libraries to make things ergonomic.++spec_mpi :: Spec+spec_mpi = do+ it "can initialize a mutable MPI reference" $ do+ _mp <- mpInit+ pass+ it "can set the value from an int" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ pass+ it "can set the value from another reference" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mp' <- mpInit+ mpSetFromMP mp' mp+ isEqual <- mpEqual mp mp'+ isEqual `shouldBe` True+ pass+ it "can be converted to a hex string" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ hex <- mpToHex mp+ hex `shouldBe` "0x0200"+ hex' <- mpToStr mp 16+ hex' `shouldBe` "0x0200"+ pass+ it "can be converted to a decimal string" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ dec <- mpToStr mp 10+ dec `shouldBe` "512"+ pass+ it "can be cleared to zero" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mpClear mp+ zero <- mpInit+ mpSetFromInt zero 0+ isClear <- mpEqual mp zero+ isClear `shouldBe` True+ pass+ it "can copy the value to a new reference" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mp' <- mpCopy mp+ isEqual <- mpEqual mp mp'+ isEqual `shouldBe` True+ pass+ it "can set the value from a decimal string" $ do+ mp <- mpInit+ mpSetFromStr mp "512"+ mp' <- mpInit+ mpSetFromInt mp' 512+ isEqualStr <- mpEqual mp mp'+ isEqualStr `shouldBe` True+ mpSetFromRadixStr mp "512" 10+ isEqualRadixStr <- mpEqual mp mp'+ isEqualRadixStr `shouldBe` True+ pass+ it "can set the value from a hex string" $ do+ mp <- mpInit+ mpSetFromStr mp "0x0200"+ mp' <- mpInit+ mpSetFromInt mp' 512+ isEqualStr <- mpEqual mp mp'+ isEqualStr `shouldBe` True+ mpSetFromRadixStr mp "0200" 16 -- NOTE: Lack of "0x" prefix+ isEqualRadixStr <- mpEqual mp mp'+ isEqualRadixStr `shouldBe` True+ pass+ it "has a number of bits" $ do+ mp <- mpInit+ mpSetFromStr mp "512"+ n <- mpNumBits mp+ n `shouldBe` 10+ pass+ it "has a number of bytes" $ do+ mp <- mpInit+ mpSetFromStr mp "512"+ n <- mpNumBytes mp+ n `shouldBe` 2+ pass+ it "can be converted to a bytestring" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ bin <- mpToBin mp+ bin `shouldBe` "\STX\NUL" -- NOTE: 0b 0000 0010 0000 0000+ pass+ it "can set the value from a bytestring" $ do+ mp <- mpInit+ mpFromBin mp "\STX\NUL"+ mp' <- mpInit+ mpSetFromInt mp' 512+ isEqual <- mpEqual mp mp'+ isEqual `shouldBe` True+ pass+ it "can be converted to a 32-bit word" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ w <- mpToWord32 mp+ w `shouldBe` 512+ pass+ -- NOTE: Returns whether x >= 0, not x > 0 because 0 is treated as positive.+ it "can check if a value is positive" $ do+ n <- mpInit+ mpSetFromInt n (-512)+ nIsPositive <- mpIsPositive n+ nIsPositive `shouldBe` False+ z <- mpInit+ mpSetFromInt z 0+ zIsPositive <- mpIsPositive z+ zIsPositive `shouldBe` True+ p <- mpInit+ mpSetFromInt p 512+ pIsPositive <- mpIsPositive p+ pIsPositive `shouldBe` True+ pass+ it "can check if a value is negative" $ do+ n <- mpInit+ mpSetFromInt n (-512)+ nIsPositive <- mpIsNegative n+ nIsPositive `shouldBe` True+ z <- mpInit+ mpSetFromInt z 0+ zIsPositive <- mpIsNegative z+ zIsPositive `shouldBe` False+ p <- mpInit+ mpSetFromInt p 512+ pIsPositive <- mpIsNegative p+ pIsPositive `shouldBe` False+ pass+ it "can flip the sign of a value" $ do+ p <- mpInit+ mpSetFromInt p 512+ n <- mpInit+ mpSetFromInt n (-512)+ mpFlipSign p+ _isEqual <- mpEqual p n+ pass+ it "can check whether a value is zero" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ isZeroA <- mpIsZero mp+ isZeroA `shouldBe` False+ mpSetFromInt mp 0+ isZeroB <- mpIsZero mp+ isZeroB `shouldBe` True+ mpSetFromInt mp (-512)+ isZeroC <- mpIsZero mp+ isZeroC `shouldBe` False+ pass+ it "can add a 32-bit word to the value and store the result" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mp' <- mpInit+ mpAddWord32 mp' mp 512+ result <- mpToWord32 mp'+ result `shouldBe` 1024+ pass+ it "can subtract a 32-bit word from the value and store the result" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mp' <- mpInit+ mpSubWord32 mp' mp 512+ result <- mpToWord32 mp'+ result `shouldBe` 0+ pass+ it "can add one value to another and store the result" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 512+ c <- mpInit+ mpAdd c a b+ result <- mpToWord32 c+ result `shouldBe` 1024+ pass+ it "can subtract one value from another and store the result" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 512+ c <- mpInit+ mpSub c a b+ result <- mpToWord32 c+ result `shouldBe` 0+ pass+ it "can multiply one value by another and store the result" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 512+ c <- mpInit+ mpMul c a b+ result <- mpToWord32 c+ result `shouldBe` 262144+ pass+ it "can divide one value by another and store the result" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 512+ q <- mpInit+ r <- mpInit+ mpDiv q r a b+ quotient <- mpToWord32 q+ quotient `shouldBe` 1+ remainder <- mpToWord32 r+ remainder `shouldBe` 0+ pass+ it "can perform multiplication and modulus at the same time and store the result" $ do+ r <- mpInit+ x <- mpInit+ mpSetFromInt x 2+ y <- mpInit+ mpSetFromInt y 4+ mod <- mpInit+ mpSetFromInt mod 7+ mpModMul r x y mod+ result <- mpToWord32 r+ result `shouldBe` 1+ pass+ it "can check whether two values are equal" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 512+ c <- mpInit+ mpSetFromInt c 256+ abEqual <- mpEqual a b+ abEqual `shouldBe` True+ bcEqual <- mpEqual b c+ bcEqual `shouldBe` False+ pass+ it "can compare two values for order" $ do+ n <- mpInit+ mpSetFromInt n (-1)+ z <- mpInit+ mpSetFromInt z 0+ p <- mpInit+ mpSetFromInt p 1+ nz <- mpCmp n z+ nz `shouldBe` -1+ zp <- mpCmp z p+ zp `shouldBe` -1+ np <- mpCmp n p+ np `shouldBe` -1+ nn <- mpCmp n n+ nn `shouldBe` 0+ zz <- mpCmp z z+ zz `shouldBe` 0+ pp <- mpCmp p p+ pp `shouldBe` 0+ pz <- mpCmp p z+ pz `shouldBe` 1+ zn <- mpCmp z n+ zn `shouldBe` 1+ pn <- mpCmp p n+ pn `shouldBe` 1+ pass+ it "can swap the values of two references" $ do+ a <- mpInit+ mpSetFromInt a 512+ b <- mpInit+ mpSetFromInt b 256+ mpSwap a b+ a' <- mpToWord32 a+ a' `shouldBe` 256+ b' <- mpToWord32 b+ b' `shouldBe` 512+ pass+ it "can perform exponentiation and modulus at the same time and store the result" $ do+ r <- mpInit+ x <- mpInit+ mpSetFromInt x 2+ y <- mpInit+ mpSetFromInt y 4+ mod <- mpInit+ mpSetFromInt mod 7+ mpPowMod r x y mod+ result <- mpToWord32 r+ result `shouldBe` 2+ pass+ it "can perform a left shift and store the result" $ do+ mp <- mpInit+ result <- mpInit+ mpSetFromInt mp 512+ mpLeftShift result mp 3+ r <- mpToWord32 result+ r `shouldBe` 4096+ pass+ it "can perform a right shift and store the result" $ do+ mp <- mpInit+ result <- mpInit+ mpSetFromInt mp 512+ mpRightShift result mp 3+ r <- mpToWord32 result+ r `shouldBe` 64+ pass+ it "can compute the modular inverse" $ do+ result <- mpInit+ x <- mpInit+ mpSetFromInt x 29+ mod <- mpInit+ mpSetFromInt mod 65535+ -- x needs to be smaller than mod+ mpModInverse result x mod+ r <- mpToWord32 result+ r `shouldBe` 13559+ pass+ it "can generate a number of random bits" $ do+ mp <- mpInit+ rng <- rngInit "system"+ mpRandBits mp rng 9+ r <- mpToWord32 mp+ 0 <= r && r < 512 `shouldBe` True+ pass+ it "can generate a random value within a range [lower,upper)" $ do+ result <- mpInit+ rng <- rngInit "system"+ lower <- mpInit+ mpSetFromInt lower 4+ upper <- mpInit+ mpSetFromInt upper 8+ mpRandRange result rng lower upper+ isGTELower <- (<= 0) <$> mpCmp lower result+ isGTELower `shouldBe` True+ isLTUpper <- (== -1) <$> mpCmp result upper+ isLTUpper `shouldBe` True+ pass+ it "can calculate the greatest common divisor of a value" $ do+ r <- mpInit+ x <- mpInit+ mpSetFromInt x 35+ y <- mpInit+ mpSetFromInt y 21+ mpGCD r x y+ result <- mpToWord32 r+ result `shouldBe` 7+ pass+ it "can probabilistically calculate whether a value is prime" $ do+ mp <- mpInit+ rng <- rngInit "system"+ mpSetFromInt mp 131+ isPrime131 <- mpIsPrime mp rng 100+ isPrime131 `shouldBe` True+ mpSetFromInt mp 133+ isPrime133 <- mpIsPrime mp rng 100+ isPrime133 `shouldBe` False+ pass+ it "can get the value of a single bit" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ f <- mpGetBit mp 8+ f `shouldBe` False+ t <- mpGetBit mp 9+ t `shouldBe` True+ pass+ it "can set the value of a single bit (set it to 1)" $ do+ mp <- mpInit+ mpSetFromInt mp 512+ mpSetBit mp 8+ result <- mpToWord32 mp+ result `shouldBe` 768+ pass+ it "can clear the value of a single bit (set it to 0)" $ do+ mp <- mpInit+ mpSetFromInt mp 768+ mpClearBit mp 8+ result <- mpToWord32 mp+ result `shouldBe` 512+ pass
+ test/Test/Botan/Low/PubKey.hs view
@@ -0,0 +1,211 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.RNG+import Control.Monad+import Data.ByteString+import Data.String+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_pubKey" spec_pubKey+ pure $ testGroup "Test.Botan.Low.PubKey" [+ specs+ ]++ecGroup :: ByteString+ecGroup = "secp256r1"++dlGroup :: ByteString+dlGroup = "modp/ietf/1024"++-- TODO: More exhaustive testing in botan+-- pks :: [(PKName, PKParams)]+pks :: [(ByteString, ByteString)]+pks =+ [ ("RSA", "2048")+ , ("SM2", ecGroup)+ , ("ElGamal", dlGroup)+ , ("DSA", dlGroup)+ , ("ECDSA", ecGroup)+ , ("ECKCDSA", ecGroup)+ , ("ECGDSA", ecGroup)+ , ("GOST-34.10", ecGroup)+ , ("Ed25519", "")+ , ("XMSS", "XMSS-SHAKE_10_256") -- NOTE: XMSS is noticeably slower than other keys and+ -- the _16 and _20 variants are exponentially slower+ , ("DH", dlGroup)+ , ("ECDH", ecGroup)+ , ("Curve25519", "")+ -- TODO: Dilithium-x-y+ , ("Dilithium", "")+ -- TODO: Kyber-x-y+ , ("Kyber", "")+ -- TODO: SPHINCS++ -- https://github.com/randombit/botan/blob/a303f4af1504e7ac349dd798190924ea08ead9b7/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp#L155+ -- , ("SPHINCS+", "sha2-128s-r3.1") -- Not working+ , ("McEliece", "")+ ]++pkTestName :: (ByteString, ByteString) -> String+pkTestName (pk, param) = chars $ pk <> " " <> param++-- NOTE: Known fields are: n, e, p, q, g, a, b+-- ECGroup PrivKey: x plus all in PubKey+-- EGGroup PubKey: public_x, public_y, base_x, base_y, p, a, b, cofactor, order+-- DLGroup PubKey: p, q, g, y (not all groups have all four)+-- DLGroup PrivKey: p, q, g, x, y+-- RSA PubKey: p, q, d, c, d1, d2+-- RSA PrivKey: n, e, d, p, q, d1, d2, c++ecGroupPrivFields :: [ByteString]+ecGroupPrivFields = [ "x" , "public_x", "public_y", "base_x", "base_y", "p", "a", "b", "cofactor", "order" ]+ecGroupPubFields :: [ByteString]+ecGroupPubFields = [ "public_x", "public_y", "base_x", "base_y", "p", "a", "b", "cofactor", "order" ]++dlGroupPrivFields :: [ByteString]+dlGroupPrivFields = [ "p", "q", "g", "x", "y" ]+dlGroupPubFields :: [ByteString]+dlGroupPubFields = [ "p", "q", "g", "y" ]++privKeyFields :: (Eq a, IsString a) => a -> [ByteString]+privKeyFields "RSA" = [ "p", "q", "d", "n", "e"] -- TODO: Check+privKeyFields "SM2" = ecGroupPrivFields+privKeyFields "ElGamal" = dlGroupPrivFields+privKeyFields "DSA" = dlGroupPrivFields+privKeyFields "ECDSA" = ecGroupPrivFields+privKeyFields "ECKCDSA" = ecGroupPrivFields+privKeyFields "ECGDSA" = ecGroupPrivFields+privKeyFields "GOST-34.10" = ecGroupPrivFields+privKeyFields "Ed25519" = [] -- TODO+privKeyFields "XMSS" = [] -- TODO+privKeyFields "DH" = dlGroupPrivFields+privKeyFields "ECDH" = ecGroupPrivFields+privKeyFields "Curve25519" = [] -- TODO+privKeyFields "Dilithium" = [] -- TODO+privKeyFields "Kyber" = [] -- TODO+privKeyFields "McEliece" = [] -- TODO+privKeyFields _ = error "privKeyFields"++pubKeyFields :: (Eq a, IsString a) => a -> [ByteString]+pubKeyFields "RSA" = [ "n", "e" ]+pubKeyFields "SM2" = ecGroupPubFields+pubKeyFields "ElGamal" = dlGroupPubFields+pubKeyFields "DSA" = dlGroupPubFields+pubKeyFields "ECDSA" = ecGroupPubFields+pubKeyFields "ECKCDSA" = ecGroupPubFields+pubKeyFields "ECGDSA" = ecGroupPubFields+pubKeyFields "GOST-34.10" = ecGroupPubFields+pubKeyFields "Ed25519" = [] -- TODO+pubKeyFields "XMSS" = [] -- TODO+pubKeyFields "DH" = dlGroupPubFields+pubKeyFields "ECDH" = ecGroupPubFields+pubKeyFields "Curve25519" = [] -- TODO+pubKeyFields "Dilithium" = [] -- TODO+pubKeyFields "Kyber" = [] -- TODO+pubKeyFields "McEliece" = [] -- TODO+pubKeyFields _ = error "pubKeyFields"++-- NOTE: These tests are going to be very slow if we create new keys every time+spec_pubKey :: Spec+spec_pubKey = testSuite pks pkTestName $ \ (pk, param) -> do+ it "privKeyCreate" $ do+ rng <- rngInit "system"+ _privKey <- privKeyCreate pk param rng+ pass+ it "privKeyCheckKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ privKeyCheckKey privKey rng CheckKeyNormalTests+ -- privKeyCheckKey privKey rng CheckKeyExpensiveTests+ pass+ it "privKeyExport" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _privKeyDER <- privKeyExport privKey PrivKeyExportDER+ _privKeyPEM <- privKeyExport privKey PrivKeyExportPEM+ pass+ it "privKeyLoad" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ privKeyDER <- privKeyExport privKey PrivKeyExportDER+ privKeyPEM <- privKeyExport privKey PrivKeyExportPEM+ void $ privKeyLoad privKeyDER ""+ void $ privKeyLoad privKeyPEM ""+ pass+ it "privKeyAlgoName" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ void $ privKeyAlgoName privKey+ pass+ it "privKeyExportPubKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _pubKey <- privKeyExportPubKey privKey+ pass+ it "pubKeyCheckKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ void $ pubKeyCheckKey pubKey rng CheckKeyNormalTests+ -- pubKeyCheckKey pubKey rng CheckKeyExpensiveTests+ pass+ it "pubKeyExport" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ _pubKeyDER <- pubKeyExport pubKey PrivKeyExportDER+ _pubKeyPEM <- pubKeyExport pubKey PrivKeyExportPEM+ pass+ it "pubKeyLoad" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ pubKeyDER <- pubKeyExport pubKey PrivKeyExportDER+ pubKeyPEM <- pubKeyExport pubKey PrivKeyExportPEM+ void $ pubKeyLoad pubKeyDER+ void $ pubKeyLoad pubKeyPEM+ pass+ it "pubKeyAlgoName" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ void $ pubKeyAlgoName pubKey+ pass+ it "pubKeyEstimatedStrength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ void $ pubKeyEstimatedStrength pubKey+ pass+ it "pubKeyFingerprint" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ void $ pubKeyFingerprint pubKey "SHA-256"+ pass+ describe "privKeyGetField" $ do+ forM_ (privKeyFields pk) $ \ field -> do+ it (chars field) $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ mp <- mpInit+ privKeyGetField mp privKey field+ pass+ describe "pubKeyGetField" $ do+ forM_ (pubKeyFields pk) $ \ field -> do+ it (chars field) $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ mp <- mpInit+ pubKeyGetField mp pubKey field+ pass
+ test/Test/Botan/Low/PubKey/DH.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.DH (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.DH+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_dh" spec_dh+ pure $ testGroup "Test.Botan.Low.PubKey.DH" [+ specs+ ]++-- TODO: Consolidate+dlGroups :: [ByteString]+dlGroups =+ [ "ffdhe/ietf/2048"+ , "ffdhe/ietf/3072"+ , "ffdhe/ietf/4096"+ , "ffdhe/ietf/6144"+ , "ffdhe/ietf/8192"+ , "modp/ietf/1024"+ , "modp/ietf/1536"+ , "modp/ietf/2048"+ , "modp/ietf/3072"+ , "modp/ietf/4096"+ , "modp/ietf/6144"+ , "modp/ietf/8192"+ , "modp/srp/1024"+ , "modp/srp/1536"+ , "modp/srp/2048"+ , "modp/srp/3072"+ , "modp/srp/4096"+ , "modp/srp/6144"+ , "modp/srp/8192"+ , "dsa/jce/1024"+ , "dsa/botan/2048"+ , "dsa/botan/3072"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++spec_dh :: Spec+spec_dh = testSuite dlGroups chars $ \ dlGroup -> do+ it "privKeyLoadDH" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DH" dlGroup rng+ p <- privKeyField privKey "p"+ g <- privKeyField privKey "g"+ x <- privKeyField privKey "x"+ _loadedKey <- privKeyLoadDH p g x+ pass+ it "pubKeyLoadDH" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DH" dlGroup rng+ pubKey <- privKeyExportPubKey privKey+ p <- pubKeyField pubKey "p"+ g <- pubKeyField pubKey "g"+ y <- pubKeyField pubKey "y"+ _loadedKey <- pubKeyLoadDH p g y+ pass
+ test/Test/Botan/Low/PubKey/DSA.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.DSA (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.DSA+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_dsa" spec_dsa+ pure $ testGroup "Test.Botan.Low.PubKey.DSA" [+ specs+ ]++-- TODO: Consolidate+dlGroups :: [ByteString]+dlGroups =+ [ "dsa/jce/1024"+ , "dsa/botan/2048"+ , "dsa/botan/3072"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++-- NOTE: Fields are+-- DLGroup PubKey: p, q, g, y+-- DLGroup PrivKey: p, q, g, x, y++-- NOTE: Fails on pubKeyLoadDSA for srp curves (but not privKeyLoadDSA?)+spec_dsa :: Spec+spec_dsa = testSuite dlGroups chars $ \ dlGroup -> do+ it "privKeyLoadDSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DSA" dlGroup rng+ p <- privKeyField privKey "p"+ q <- privKeyField privKey "q"+ g <- privKeyField privKey "g"+ x <- privKeyField privKey "x"+ _loadedKey <- privKeyLoadDSA p q g x+ pass+ it "pubKeyLoadDSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DSA" dlGroup rng+ pubKey <- privKeyExportPubKey privKey+ p <- pubKeyField pubKey "p"+ q <- pubKeyField pubKey "q"+ g <- pubKeyField pubKey "g"+ y <- pubKeyField pubKey "y"+ _loadedKey <- pubKeyLoadDSA p q g y+ pass
+ test/Test/Botan/Low/PubKey/Decrypt.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.Decrypt (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.Decrypt+import Botan.Low.PubKey.Encrypt+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_decrypt" spec_decrypt+ pure $ testGroup "Test.Botan.Low.PubKey.Decrypt" [+ specs+ ]++pks :: [(ByteString, ByteString, ByteString)]+pks =+ [ ("RSA", "2048", "PKCS1v15")+ , ("SM2", "sm2p256v1", "SHA-256") -- NOTE: SM2 takes a hash rather than a padding+ , ("ElGamal", "modp/ietf/1024", "PKCS1v15")+ ]++pkTestName :: (ByteString, ByteString, ByteString) -> String+pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding++spec_decrypt :: Spec+spec_decrypt = testSuite pks pkTestName $ \ (pk, param, padding) -> do+ it "decryptCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _pubKey <- privKeyExportPubKey privKey+ _ctx <- decryptCreate privKey padding+ pass+ it "decryptOutputLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _pubKey <- privKeyExportPubKey privKey+ ctx <- decryptCreate privKey padding+ _ <- decryptOutputLength ctx 128+ pass+ it "decrypt" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ ectx <- encryptCreate pubKey padding+ dctx <- decryptCreate privKey padding+ encrypted <- encrypt ectx rng "Fee fi fo fum!"+ decrypted <- decrypt dctx encrypted+ decrypted `shouldBe` "Fee fi fo fum!"+ pass
+ test/Test/Botan/Low/PubKey/ECDH.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.ECDH (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.ECDH+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_ecdh" spec_ecdh+ pure $ testGroup "Test.Botan.Low.PubKey.ECDH" [+ specs+ ]++-- TODO: Consolidate+ecGroups :: [ByteString]+ecGroups =+ [ "secp160k1"+ , "secp160r1"+ , "secp160r2"+ , "secp192k1"+ , "secp192r1"+ , "secp224k1"+ , "secp224r1"+ , "secp256k1"+ , "secp256r1"+ , "secp384r1"+ , "secp521r1"+ , "brainpool160r1"+ , "brainpool192r1"+ , "brainpool224r1"+ , "brainpool256r1"+ , "brainpool320r1"+ , "brainpool384r1"+ , "brainpool512r1"+ , "x962_p192v2"+ , "x962_p192v3"+ , "x962_p239v1"+ , "x962_p239v2"+ , "x962_p239v3"+ , "gost_256A"+ , "gost_512A"+ , "frp256v1"+ , "sm2p256v1"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++spec_ecdh :: Spec+spec_ecdh = testSuite ecGroups chars $ \ ecGroup -> do+ it "privKeyLoadECDH" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "ECDH" ecGroup rng+ x <- privKeyField privKey "x"+ _loadedPrivKey <- privKeyLoadECDH x ecGroup+ -- Inscrutible >_> but shows key equality+ -- pubKeyBytes <- privKeyExportPubKey privKey >>= \ pubKey -> pubKeyExport pubKey PubKeyExportPEM+ -- loadedPubKeyBytes <- privKeyExportPubKey loadedPrivKey >>= \ loadedPubKey -> pubKeyExport loadedPubKey PubKeyExportPEM+ -- pubKeyBytes `shouldBe` loadedPubKeyBytes+ pass+ it "pubKeyLoadECDH" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "ECDH" ecGroup rng+ pubKey <- privKeyExportPubKey privKey+ public_x <- pubKeyField pubKey "public_x"+ public_y <- pubKeyField pubKey "public_y"+ _loadedPubKey <- pubKeyLoadECDH public_x public_y ecGroup+ -- pubKeyBytes <- pubKeyExport pubKey PubKeyExportPEM+ -- loadedPubKeyBytes <- pubKeyExport loadedPubKey PubKeyExportPEM+ -- pubKeyBytes `shouldBe` loadedPubKeyBytes+ pass
+ test/Test/Botan/Low/PubKey/ECDSA.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.ECDSA (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.ECDSA+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_ecdsa" spec_ecdsa+ pure $ testGroup "Test.Botan.Low.PubKey.ECDSA" [+ specs+ ]++-- TODO: Consolidate+ecGroups :: [ByteString]+ecGroups =+ [ "secp160k1"+ , "secp160r1"+ , "secp160r2"+ , "secp192k1"+ , "secp192r1"+ , "secp224k1"+ , "secp224r1"+ , "secp256k1"+ , "secp256r1"+ , "secp384r1"+ , "secp521r1"+ , "brainpool160r1"+ , "brainpool192r1"+ , "brainpool224r1"+ , "brainpool256r1"+ , "brainpool320r1"+ , "brainpool384r1"+ , "brainpool512r1"+ , "x962_p192v2"+ , "x962_p192v3"+ , "x962_p239v1"+ , "x962_p239v2"+ , "x962_p239v3"+ , "gost_256A"+ , "gost_512A"+ , "frp256v1"+ , "sm2p256v1"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++-- TODO: Don't assume just "ECDSA", other elliptic curve DSA might be valid here+spec_ecdsa :: Spec+spec_ecdsa = testSuite ecGroups chars $ \ ecGroup -> do+ it "privKeyLoadECDSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "ECDSA" ecGroup rng+ x <- privKeyField privKey "x"+ _loadedPrivKey <- privKeyLoadECDSA x ecGroup+ pass+ it "pubKeyLoadECDSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "ECDSA" ecGroup rng+ pubKey <- privKeyExportPubKey privKey+ public_x <- pubKeyField pubKey "public_x"+ public_y <- pubKeyField pubKey "public_y"+ _loadedPubKey <- pubKeyLoadECDSA public_x public_y ecGroup+ pass
+ test/Test/Botan/Low/PubKey/Ed25519.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.Ed25519 (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.Ed25519+import Botan.Low.RNG+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_ed25519" spec_ed25519+ pure $ testGroup "Test.Botan.Low.PubKey.Ed25519" [+ specs+ ]++spec_ed25519 :: Spec+spec_ed25519 = do+ it "privKeyEd25519GetPrivKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Ed25519" "" rng+ _exportedPrivKey <- privKeyEd25519GetPrivKey privKey+ pass+ it "pubKeyEd25519GetPubKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Ed25519" "" rng+ pubKey <- privKeyExportPubKey privKey+ _exportedPubKey <- pubKeyEd25519GetPubKey pubKey+ pass+ it "privKeyLoadEd25519" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Ed25519" "" rng+ exportedPrivKey <- privKeyEd25519GetPrivKey privKey+ _loadedPrivKey <- privKeyLoadEd25519 exportedPrivKey+ pass+ it "pubKeyLoadEd25519" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Ed25519" "" rng+ pubKey <- privKeyExportPubKey privKey+ exportedPubKey <- pubKeyEd25519GetPubKey pubKey+ _loadedPubKey <- pubKeyLoadEd25519 exportedPubKey+ pass
+ test/Test/Botan/Low/PubKey/ElGamal.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.ElGamal (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.ElGamal+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_elGamal" spec_elGamal+ pure $ testGroup "Test.Botan.Low.PubKey.ElGamal" [+ specs+ ]+++-- TODO: Consolidate+dlGroups :: [ByteString]+dlGroups =+ [ "ffdhe/ietf/2048"+ , "ffdhe/ietf/3072"+ , "ffdhe/ietf/4096"+ , "ffdhe/ietf/6144"+ , "ffdhe/ietf/8192"+ , "modp/ietf/1024"+ , "modp/ietf/1536"+ , "modp/ietf/2048"+ , "modp/ietf/3072"+ , "modp/ietf/4096"+ , "modp/ietf/6144"+ , "modp/ietf/8192"+ , "modp/srp/1024"+ , "modp/srp/1536"+ , "modp/srp/2048"+ , "modp/srp/3072"+ , "modp/srp/4096"+ , "modp/srp/6144"+ , "modp/srp/8192"+ , "dsa/jce/1024"+ , "dsa/botan/2048"+ , "dsa/botan/3072"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++spec_elGamal :: Spec+spec_elGamal = testSuite dlGroups chars $ \ dlGroup -> do+ it "privKeyLoadELGamal" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DH" dlGroup rng+ p <- privKeyField privKey "p"+ _q <- privKeyField privKey "q"+ g <- privKeyField privKey "g"+ x <- privKeyField privKey "x"+ _loadedKey <- privKeyLoadElGamal p g x+ pass+ it "pubKeyLoadELGamal" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "DH" dlGroup rng+ _pubKey <- privKeyExportPubKey privKey+ p <- privKeyField privKey "p"+ _q <- privKeyField privKey "q"+ g <- privKeyField privKey "g"+ y <- privKeyField privKey "y"+ _loadedKey <- pubKeyLoadElGamal p g y+ pass
+ test/Test/Botan/Low/PubKey/Encrypt.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.Encrypt (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.Encrypt+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_encrypt" spec_encrypt+ pure $ testGroup "Test.Botan.Low.PubKey.Encrypt" [+ specs+ ]++pks :: [(ByteString, ByteString, ByteString)]+pks =+ [ ("RSA", "2048", "PKCS1v15")+ , ("SM2", "sm2p256v1", "SHA-256") -- NOTE: SM2 takes a hash rather than a padding+ , ("ElGamal", "modp/ietf/1024", "PKCS1v15")+ ]++pkTestName :: (ByteString, ByteString, ByteString) -> String+pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding++spec_encrypt :: Spec+spec_encrypt = testSuite pks pkTestName $ \ (pk, param, padding) -> do+ it "encryptCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ _ctx <- encryptCreate pubKey padding+ pass+ it "encryptOutputLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ ctx <- encryptCreate pubKey padding+ _ <- encryptOutputLength ctx 128+ pass+ it "encrypt" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ ctx <- encryptCreate pubKey padding+ _ <- encrypt ctx rng "Fee fi fo fum!"+ pass
+ test/Test/Botan/Low/PubKey/KeyAgreement.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.KeyAgreement (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.KeyAgreement+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_keyAgreement" spec_keyAgreement+ pure $ testGroup "Test.Botan.Low.PubKey.KeyAgreement" [+ specs+ ]++-- TODO: More thorough test with different KDFs and curves / groups+pks :: [(ByteString, ByteString)]+pks =+ [ ("DH", "modp/ietf/1024")+ , ("ECDH", "secp256r1")+ , ("Curve25519", "")+ ]++pkTestName :: (ByteString, ByteString) -> String+pkTestName (pk, param) = chars $ pk <> " " <> param++spec_keyAgreement :: Spec+spec_keyAgreement = testSuite pks pkTestName $ \ (pk, param) -> do+ it "keyAgreementCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _ka <- keyAgreementCreate privKey "HKDF(SHA-256)"+ pass+ it "keyAgreementExportPublic" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _ka <- keyAgreementCreate privKey "HKDF(SHA-256)"+ _pubKey <- keyAgreementExportPublic privKey+ pass+ it "keyAgreementSize" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ ka <- keyAgreementCreate privKey "HKDF(SHA-256)"+ _kaSz <- keyAgreementSize ka+ pass+ it "keyAgreement" $ do+ rng <- rngInit "system"+ privKeyA <- privKeyCreate pk param rng+ pubKeyA <- keyAgreementExportPublic privKeyA+ privKeyB <- privKeyCreate pk param rng+ pubKeyB <- keyAgreementExportPublic privKeyB+ kaA <- keyAgreementCreate privKeyA "HKDF(SHA-256)"+ kaB <- keyAgreementCreate privKeyB "HKDF(SHA-256)"+ sharedKeyA <- keyAgreement kaA pubKeyB "salt"+ sharedKeyB <- keyAgreement kaB pubKeyA "salt"+ sharedKeyA `shouldBe` sharedKeyB+ pass
+ test/Test/Botan/Low/PubKey/KeyEncapsulation.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.KeyEncapsulation (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.KeyEncapsulation+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_keyEncapsulation" spec_keyEncapsulation+ pure $ testGroup "Test.Botan.Low.PubKey.KeyEncapsulation" [+ specs+ ]+++pks :: [(ByteString, ByteString, ByteString)]+pks =+ [ ( "RSA", "2048", "HKDF(SHA-256)")+ , ( "Kyber", "", "HKDF(SHA-256)")+ , ( "McEliece", "", "HKDF(SHA-256)")+ ]++pkTestName :: (ByteString, ByteString, ByteString) -> String+pkTestName (pk, param, kdf) = chars $ pk <> " " <> param <> " " <> kdf++spec_keyEncapsulation :: Spec+spec_keyEncapsulation = testSuite pks pkTestName $ \ (pk, param, _kdf) -> do+ it "kemEncryptCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ _kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"+ pass+ it "kemEncryptSharedKeyLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"+ _sharedKeyLength <- kemEncryptSharedKeyLength kem 256+ pass+ it "kemEncryptEncapsulatedKeyLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"+ _encapsulatedKeyLength <- kemEncryptEncapsulatedKeyLength kem+ pass+ it "kemEncryptCreateSharedKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ kem <- kemEncryptCreate pubKey "HKDF(SHA-256)"+ (_sharedKey, _encapsulatedKey) <- kemEncryptCreateSharedKey kem rng "salt" 256+ pass+ it "kemDecryptCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _kem <- kemDecryptCreate privKey "HKDF(SHA-256)"+ pass+ it "kemDecryptSharedKeyLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ kem <- kemDecryptCreate privKey "HKDF(SHA-256)"+ _sharedKeyLen <- kemDecryptSharedKeyLength kem 256+ pass+ it "kemDecryptSharedKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ encryptCtx <- kemEncryptCreate pubKey "HKDF(SHA-256)"+ (sharedKey, encapsulatedKey) <- kemEncryptCreateSharedKey encryptCtx rng "salt" 256+ decryptCtx <- kemDecryptCreate privKey "HKDF(SHA-256)"+ decryptedSharedKey <- kemDecryptSharedKey decryptCtx "salt" encapsulatedKey 256+ sharedKey `shouldBe` decryptedSharedKey+ pass
+ test/Test/Botan/Low/PubKey/RSA.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.RSA (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.RSA+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_rsa" spec_rsa+ pure $ testGroup "Test.Botan.Low.PubKey.RSA" [+ specs+ ]++rsaSizes :: [ByteString]+rsaSizes = [ "2048" ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++spec_rsa :: Spec+spec_rsa = testSuite rsaSizes chars $ \ rsaSize -> do+ it "privKeyLoadRSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "RSA" rsaSize rng+ p <- privKeyField privKey "p"+ q <- privKeyField privKey "q"+ e <- privKeyField privKey "e"+ _loadedKey <- privKeyLoadRSA p q e+ pass+ it "pubKeyLoadRSA" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "RSA" rsaSize rng+ pubKey <- privKeyExportPubKey privKey+ n <- pubKeyField pubKey "n"+ e <- pubKeyField pubKey "e"+ _loadedKey <- pubKeyLoadRSA n e+ pass
+ test/Test/Botan/Low/PubKey/SM2.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.SM2 (tests) where++import Botan.Low.MPI+import Botan.Low.PubKey+import Botan.Low.PubKey.SM2+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_sm2" spec_sm2+ pure $ testGroup "Test.Botan.Low.PubKey.SM2" [+ specs+ ]++-- TODO: Consolidate+ecGroups :: [ByteString]+ecGroups =+ [ "secp160k1"+ , "secp160r1"+ , "secp160r2"+ , "secp192k1"+ , "secp192r1"+ , "secp224k1"+ , "secp224r1"+ , "secp256k1"+ , "secp256r1"+ , "secp384r1"+ , "secp521r1"+ , "brainpool160r1"+ , "brainpool192r1"+ , "brainpool224r1"+ , "brainpool256r1"+ , "brainpool320r1"+ , "brainpool384r1"+ , "brainpool512r1"+ , "x962_p192v2"+ , "x962_p192v3"+ , "x962_p239v1"+ , "x962_p239v2"+ , "x962_p239v3"+ , "gost_256A"+ , "gost_512A"+ , "frp256v1"+ , "sm2p256v1"+ ]++-- TODO: Consolidate+privKeyField :: PrivKey -> ByteString -> IO MP+privKeyField privKey field = do+ mp <- mpInit+ privKeyGetField mp privKey field+ return mp++-- TODO: Consolidate+pubKeyField :: PubKey -> ByteString -> IO MP+pubKeyField pubKey field = do+ mp <- mpInit+ pubKeyGetField mp pubKey field+ return mp++spec_sm2 :: Spec+spec_sm2 = testSuite ecGroups chars $ \ ecGroup -> do+ it "privKeyLoadSM2" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "SM2" ecGroup rng+ x <- privKeyField privKey "x"+ _loadedPrivKey <- privKeyLoadSM2 x ecGroup+ pass+ it "pubKeyLoadSM2" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "SM2" ecGroup rng+ pubKey <- privKeyExportPubKey privKey+ public_x <- pubKeyField pubKey "public_x"+ public_y <- pubKeyField pubKey "public_y"+ _loadedPubKey <- pubKeyLoadSM2 public_x public_y ecGroup+ pass
+ test/Test/Botan/Low/PubKey/Sign.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.Sign (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.Sign+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_sign" spec_sign+ pure $ testGroup "Test.Botan.Low.PubKey.Sign" [+ specs+ ]++ecGroup :: ByteString+ecGroup = "secp256r1"++dlGroup :: ByteString+dlGroup = "modp/ietf/1024"++sha :: ByteString+sha = "SHA-256"++-- TODO: More exhaustive testing in botan+-- pks :: [(PKName, PKParams, Padding)]+pks :: [(ByteString, ByteString, ByteString)]+pks =+ [ ("RSA", "2048", "EMSA4(SHA-256)") -- NOTE: It is not yet known if any of the others suppport EMSA+ , ("SM2", "sm2p256v1", "foo,SHA-256")+ , ("DSA", dlGroup, sha)+ , ("ECDSA", ecGroup, sha)+ , ("ECKCDSA", ecGroup, sha)+ , ("ECGDSA", ecGroup, sha)+ , ("GOST-34.10", ecGroup, sha)+ , ("Ed25519", "", "")+ , ("XMSS", "XMSS-SHAKE_10_256", "")+ -- TODO: Dilithium-x-y+ , ("Dilithium", "", "")+ -- TODO: SPHINCS++ ]++pkTestName :: (ByteString, ByteString, ByteString) -> String+pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding++-- NOTE: Some algorithms do not support DER format encoding.+spec_sign :: Spec+spec_sign = testSuite pks pkTestName $ \ (pk, param, algo) -> do+ it "signCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ _ctx <- signCreate privKey algo StandardFormatSignature+ -- ctxDER <- signCreate privKey algo DERFormatSignature+ pass+ it "signOutputLength" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ ctx <- signCreate privKey algo StandardFormatSignature+ _sigLen <- signOutputLength ctx+ -- ctxDER <- signCreate privKey algo DERFormatSignature+ -- _ <- signOutputLength ctxDER+ pass+ it "signUpdate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ ctx <- signCreate privKey algo StandardFormatSignature+ signUpdate ctx "Fee fi fo fum!"+ pass+ it "signFinish" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ ctx <- signCreate privKey algo StandardFormatSignature+ signUpdate ctx "Fee fi fo fum!"+ _sig <- signFinish ctx rng+ pass
+ test/Test/Botan/Low/PubKey/Verify.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.Verify (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.Sign+import Botan.Low.PubKey.Verify+import Botan.Low.RNG+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_verify" spec_verify+ pure $ testGroup "Test.Botan.Low.PubKey.Verify" [+ specs+ ]++-- TODO: Consolidate test values with SignSpec++ecGroup :: ByteString+ecGroup = "secp256r1"++dlGroup :: ByteString+dlGroup = "modp/ietf/1024"++sha :: ByteString+sha = "SHA-256"++pks :: [(ByteString, ByteString, ByteString)]+pks =+ [ ("RSA", "2048", "EMSA4(SHA-256)")+ , ("SM2", "sm2p256v1", "foo,SHA-256")+ , ("DSA", dlGroup, sha)+ , ("ECDSA", ecGroup, sha)+ , ("ECKCDSA", ecGroup, sha)+ , ("ECGDSA", ecGroup, sha)+ , ("GOST-34.10", ecGroup, sha)+ , ("Ed25519", "", "")+ , ("XMSS", "XMSS-SHAKE_10_256", "")+ -- TODO: Dilithium-x-y+ , ("Dilithium", "", "")+ -- TODO: SPHINCS++ ]++pkTestName :: (ByteString, ByteString, ByteString) -> String+pkTestName (pk, param, padding) = chars $ pk <> " " <> param <> " " <> padding++-- SEE: signFinish for more details+-- NOTE: Verified signatures pass for all algorithms in standard format+-- Fails for only 4 in DER format via BadParameterException, but for important algs:+-- RSA, Ed25519, XMSS, Dilithium+-- The context and signatures are created without throwing any errors,+-- compared to earlier which failed to even create a context, but+-- it is possible that I need to figure out correct parameters and I doubt+-- it is coincidence that the EMSA4 / empty params are the ones that are failing.+-- Yet, it works for standard format.++spec_verify :: Spec+spec_verify = testSuite pks pkTestName $ \ (pk, param, algo) -> do+ it "verifyCreate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ _ctx <- verifyCreate pubKey algo StandardFormatSignature+ pass+ it "verifyUpdate" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ pubKey <- privKeyExportPubKey privKey+ ctx <- verifyCreate pubKey algo StandardFormatSignature+ verifyUpdate ctx "Fee fi fo fum!"+ pass+ it "verifyFinish" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate pk param rng+ signCtx <- signCreate privKey algo StandardFormatSignature+ signUpdate signCtx "Fee fi fo fum!"+ sig <- signFinish signCtx rng+ -- print $ "Siglen is: " <> show (ByteString.length sig)+ print sig+ pubKey <- privKeyExportPubKey privKey+ verifyCtx <- verifyCreate pubKey algo StandardFormatSignature+ verifyUpdate verifyCtx "Fee fi fo fum!"+ verified <- verifyFinish verifyCtx sig+ verified `shouldBe` True+ pass
+ test/Test/Botan/Low/PubKey/X25519.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PubKey.X25519 (tests) where++import Botan.Low.PubKey+import Botan.Low.PubKey.X25519+import Botan.Low.RNG+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_x25519" spec_x25519+ pure $ testGroup "Test.Botan.Low.PubKey.X25519" [+ specs+ ]++spec_x25519 :: Spec+spec_x25519 = do+ it "privKeyX25519GetPrivKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Curve25519" "" rng+ _exportedPrivKey <- privKeyX25519GetPrivKey privKey+ pass+ it "pubKeyX25519GetPubKey" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Curve25519" "" rng+ pubKey <- privKeyExportPubKey privKey+ _exportedPubKey <- pubKeyX25519GetPubKey pubKey+ pass+ it "privKeyLoadX25519" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Curve25519" "" rng+ exportedPrivKey <- privKeyX25519GetPrivKey privKey+ _loadedPrivKey <- privKeyLoadX25519 exportedPrivKey+ pass+ it "pubKeyLoadX25519" $ do+ rng <- rngInit "system"+ privKey <- privKeyCreate "Curve25519" "" rng+ pubKey <- privKeyExportPubKey privKey+ exportedPubKey <- pubKeyX25519GetPubKey pubKey+ _loadedPubKey <- pubKeyLoadX25519 exportedPubKey+ pass
+ test/Test/Botan/Low/PwdHash.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.PwdHash (+ tests+ -- * Test parameters+ , generateTestParams+ ) where++import Botan.Low.Error+import Botan.Low.Hash+import Botan.Low.MAC+import Botan.Low.PwdHash+import Control.Exception+import Control.Monad+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Tasty.HUnit+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_pwdhash" spec_pwdhash+ pure $ testGroup "Test.Botan.Low.PwdHash" [+ testCase "test_pwdhash_badSchemeName" $+ test_pwdhash_badSchemeName False+ , testCase "test_pwdhashTimed_badSchemeName" $+ test_pwdhash_badSchemeName True+ , specs+ ]++{-------------------------------------------------------------------------------+ Test parameters+-------------------------------------------------------------------------------}++data TestParams = TestParams {+ testPbkdfName :: PBKDFName+ , testIterationsParam :: Int+ , testParallelismParam :: Int+ , testMemoryParam :: Int+ }+ deriving stock (Show, Eq)++-- | Parameters for testing+--+-- These are generated using 'generateTestParams'.+testParams :: [TestParams]+testParams =+ [ TestParams "PBKDF2(SHA-256)" 436000 0 0+ , TestParams "PBKDF2(SHA-512)" 258000 0 0+ , TestParams "Scrypt" 8 1 65536+ , TestParams "Argon2d" 1 1 262144+ , TestParams "Argon2i" 1 1 262144+ , TestParams "Argon2id" 1 1 262144+ , TestParams "Bcrypt-PBKDF" 92 0 0+ , TestParams "OpenPGP-S2K(SHA-384)" 65011712 0 0+ ]++-- | Schemes for testing+testPbkdfs :: [PBKDFName]+testPbkdfs = [+ -- PBKDF2+ pbkdf2 SHA256+ , pbkdf2 SHA512+ -- Scrypt+ , Scrypt+ -- Argon+ , Argon2d+ , Argon2i+ , Argon2id+ -- Bcrypt+ , Bcrypt_PBKDF+ -- OpenPGP S2K+ , openPGP_S2K SHA384+ ]++-- | Generate test parameters using 'tuneParams' on schemes listed in+-- 'testPbkdfs'.+--+-- NOTE: the test parameters can change per invocation, but theys should be+-- roughly similar.+generateTestParams :: IO [TestParams]+generateTestParams = forM testPbkdfs tuneParams++-- | Tune parameters for a given scheme+tuneParams :: PBKDFName -> IO TestParams+tuneParams name = do+ (iterations, parallelism, memory, _) <- pwdhashTimed name 200 64 passphrase salt+ pure $ TestParams name iterations parallelism memory++passphrase :: ByteString+passphrase = "Fee fi fo fum!"++salt :: ByteString+salt = "salt"++{-------------------------------------------------------------------------------+ Tests+-------------------------------------------------------------------------------}++-- | Test that using 'pwdhash' or 'pwdhashTimed' with bad scheme names results+-- in errors.+test_pwdhash_badSchemeName :: Bool -> Assertion+test_pwdhash_badSchemeName useTimed = do+ -- PBKDF2+ go PBKDF2 >>= \case+ Left BadParameterException{} -> pure ()+ Right{} -> assertFailure "got success, but expected BadParameterException"+ go (pbkdf2 HMAC) >>= \case+ Left NotImplementedException{} -> pure ()+ Right{} -> assertFailure "got success, but expected NotImplementedException"+ go (pbkdf2 $ hmac "2") >>= \case+ Left NotImplementedException{} -> pure ()+ Right{} -> assertFailure "got success, but expected NotImplementedException"+ go (pbkdf2 $ hmac "SHA-256") >>= \case+ Left SomeException{} -> assertFailure "got SomeException, but expected success"+ Right{} -> pure ()+ go (pbkdf2 $ gmac "SHA-256") >>= \case+ Left NotImplementedException{} -> pure ()+ Right{} -> assertFailure "got success, but expected NotImplementedException"+ go (pbkdf2 "SHA-256") >>= \case+ Left SomeException{} -> assertFailure "got SomeException, but expected success"+ Right{} -> pure ()++ -- OpenPGP S2K+ go OpenPGP_S2K >>= \case+ Left NotImplementedException{} -> pure ()+ Right{} -> assertFailure "got success, but expected NotImplementedException"+ go (openPGP_S2K "2") >>= \case+ Left NotImplementedException{} -> pure ()+ Right{} -> assertFailure "got success, but expected NotImplementedException"+ go (openPGP_S2K "SHA-384") >>= \case+ Left SomeException{} -> assertFailure "got SomeException, but expected success"+ Right{} -> pure ()+ where+ go schemeName+ | useTimed = try $+ void $ pwdhashTimed schemeName 200 64 passphrase salt+ | otherwise = try $+ void $ pwdhash schemeName 1 0 0 64 passphrase salt++{-------------------------------------------------------------------------------+ Specs+-------------------------------------------------------------------------------}++-- | Run 'pwdhash' and 'pwdhashTimed', and check that their outputs match.+spec_pwdhash :: Spec+spec_pwdhash =+ testSuite testParams (\(TestParams n _ _ _) -> chars n) $+ \params@(TestParams pbkdf _ _ _) -> do+ it "pwdhash" $ do+ _ <- pwdhashCorrected params+ pass+ it "pwdhashTimed" $ do+ _timed@(iterations', parallelism', memory', pwd) <- pwdhashTimed pbkdf 200 64 passphrase salt+ pwd' <- pwdhashCorrected (TestParams pbkdf iterations' parallelism' memory')+ pwd `shouldBe` pwd'+ pass+ where+ -- For @Argon2@ and @Scrypt@ algorithms, 'botan_pwdhash_timed' returns+ -- parameters in a different order than what 'botan_pwdhash' takes in.+ pwdhashCorrected :: TestParams -> IO ByteString+ pwdhashCorrected (TestParams pbkdf iterations parallelism memory) =+ case pbkdf of+ "Scrypt" -> pwdhash pbkdf memory iterations parallelism 64 passphrase salt+ _ | "Argon" `isPrefixOf` pbkdf -> pwdhash pbkdf memory iterations parallelism 64 passphrase salt+ _ -> pwdhash pbkdf iterations parallelism memory 64 passphrase salt
+ test/Test/Botan/Low/RNG.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.RNG (tests) where++import Botan.Low.RNG+import Data.ByteString as BS+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_rng" spec_rng+ pure $ testGroup "Test.Botan.Low.RNG" [+ specs+#ifdef darwin_HOST_OS+ -- TODO: temporarily disabled because the test suite fails. See issue+ -- #33.+ | False+#endif+ ]++rngs :: [RNGType]+rngs =+ [ SystemRNG+ , UserRNG+ , UserThreadsafeRNG+ , RDRandRNG -- NOTES: Not available on all processors+ ]++spec_rng :: Spec+spec_rng = do+ it "systemRNGGet" $ do+ bs <- systemRNGGet 8+ BS.length bs `shouldBe` 8+ testSuite rngs chars $ \ rng -> do+ it "rngInit" $ do+ _ctx <- rngInit rng+ pass+ it "rngGet" $ do+ ctx <- rngInit rng+ _bs <- rngGet ctx 8+ pass+ it "rngReseed" $ do+ ctx <- rngInit rng+ rngReseed ctx 64+ pass+ it "rngReseedFromRNGCtx" $ do+ ctx <- rngInit rng+ source <- rngInit rng+ rngReseedFromRNG ctx source 64+ pass+ it "rngAddEntropy" $ do+ ctx <- rngInit rng+ rngAddEntropy ctx "Fee fi fo fum!"+ pass
+ test/Test/Botan/Low/SRP6.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.SRP6 (+ tests+ ) where++import Botan.Low.Hash+import Botan.Low.PubKey+import Botan.Low.RNG+import Botan.Low.SRP6+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_srp6" spec_srp6+ pure $ testGroup "Test.Botan.Low.SRP6" [+ specs+ ]++username :: ByteString+username = "username"++password :: ByteString+password = "password"++salt :: ByteString+salt = "saltsaltsaltsalt"++-- NOTE: Consolidate with DLGroup+groupIds :: [DLGroupName]+groupIds =+ [ "ffdhe/ietf/2048"+ , "ffdhe/ietf/3072"+ , "ffdhe/ietf/4096"+ , "ffdhe/ietf/6144"+ , "ffdhe/ietf/8192"+ , "modp/ietf/1024"+ , "modp/ietf/1536"+ , "modp/ietf/2048"+ , "modp/ietf/3072"+ , "modp/ietf/4096"+ , "modp/ietf/6144"+ , "modp/ietf/8192"+ , "modp/srp/1024"+ , "modp/srp/1536"+ , "modp/srp/2048"+ , "modp/srp/3072"+ , "modp/srp/4096"+ , "modp/srp/6144"+ , "modp/srp/8192"+ , "dsa/jce/1024"+ , "dsa/botan/2048"+ , "dsa/botan/3072"+ ]++-- TODO: Test which hashes work+hashId :: HashName+hashId = "SHA-256"++spec_srp6 :: Spec+spec_srp6 = testSuite groupIds chars $ \ groupId -> do+ it "can negotiate a shared secret" $ do+ rng <- rngInit "system"+ verifier <- srp6GenerateVerifier username password salt groupId hashId+ ctx <- srp6ServerSessionInit+ b <- srp6ServerSessionStep1 ctx verifier groupId hashId rng+ (a,sharedSecret) <- srp6ClientAgree username password groupId hashId salt b rng+ sharedSecret' <- srp6ServerSessionStep2 ctx groupId a+ sharedSecret `shouldBe` sharedSecret'+ pass
+ test/Test/Botan/Low/SRP6/Example.hs view
@@ -0,0 +1,202 @@+{-# OPTIONS_GHC -threaded -Wall #-}+{-# LANGUAGE OverloadedStrings #-}++-- | The executable example described int the Haddock documentation of+-- "Botan.Low.SRP6" module.+--+-- NOTE: when changing this example, also change the Haddock documentation in+-- the "Botan.Low.SRP6" module.+module Test.Botan.Low.SRP6.Example (main) where++{-+This is a simplified, executable example that should give you an idea of how to+use the functions in this module to run the SRP6 protocol. We will not explain+the protocol in detail here, so consult other sources if you need details about+the algorithm. The simplified example shows how a client and server run the+protocol from separate threads, keeping private values secret and communicating+public values using message passing. In practice, the client and server will+probably run on separate machines (or processes) with secure communication+channels in place, and other security measures are necessary. So, don't use this+example directly in your code if it runs in production.+-}++{-+Let us start with some necessary imports.+-}++import Botan.Low.Hash+import Botan.Low.PubKey+import Botan.Low.RNG+import Botan.Low.SRP6+import Control.Concurrent+import Control.Concurrent.Async+import Data.ByteString hiding (group)+import Data.IORef+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe+import Text.Printf++{-+The client and server exchange a number of messages during the protocol. They+will be explained in more detail later as they become relevant for the part of+the protocol that we are discussing. The messages will be exchanged from the+client to the server using an @MVar ClientMsg@, and from the server to the+client using an @MVar ServerMsg@.+-}++type Identifier = ByteString+data ClientMsg =+ Signup Identifier SRP6Verifier+ | StartAuthentication Identifier+ | SendClientPublicKey SRP6AValue+data ServerMsg =+ SendServerPublicKey SRP6BValue++{-+SRP works in a discrete logarithm group, and it computes hashes using a hash+family. Both can be configured, and we will configure them as follows:+-}++group :: DLGroupName+group = MODP_SRP_4096+hash :: HashName+hash = SHA512++{-+When a client wants to sign up with the server with a combination of an+identifier and password, the client first generates a verifier using the+identifier, the password, and a locally generated salt. Then, the client sends+their identifier and verifier to the server, while it stores its own salt+locally:+-}++type Password = ByteString+type Salt = ByteString+clientSignup :: MVar ClientMsg -> Identifier -> Password -> IO Salt+clientSignup clientMsgVar identifier password = do+ rng <- rngInit UserRNG+ salt <- rngGet rng 12+ verifier <- srp6GenerateVerifier identifier password salt group hash+ putMVar clientMsgVar $ Signup identifier verifier+ pure salt++{-+The server receives the signup request, and stores the identifier with the+verifier:+-}++type Users = Map Identifier SRP6Verifier+serverSignup :: MVar ClientMsg -> IORef Users -> IO ()+serverSignup clientMsgVar usersRef = do+ Signup identifier verifier <- takeMVar clientMsgVar+ modifyIORef usersRef $ Map.insert identifier verifier++{-+Later, the client may request to authenticate itself to the server:+-}++clientStartAuthentication :: MVar ClientMsg -> Identifier -> IO ()+clientStartAuthentication clientMsgVar identifier = do+ putMVar clientMsgVar $ StartAuthentication identifier++{-+The server receives the authentication request, looks up the verfier, generates+the server's public key (a SRP6 @B@ value), and sends it back to the client:+-}++serverStartAuthentication :: MVar ClientMsg -> MVar ServerMsg -> IORef Users -> IO SRP6ServerSession+serverStartAuthentication clientMsgVar serverMsg usersRef = do+ StartAuthentication identifier <- takeMVar clientMsgVar+ users <- readIORef usersRef+ let verifier = fromJust $ Map.lookup identifier users+ rng <- rngInit UserRNG+ session <- srp6ServerSessionInit+ serverPublicKey <- srp6ServerSessionStep1 session verifier group hash rng+ putMVar serverMsg $ SendServerPublicKey serverPublicKey+ pure session++{-+Once the client receives the server's public key, it generates the client's+public key (an SRP6 @B@ value) and the shared secret key, and sends the client's+public key to the server:+-}++clientAuthentication :: MVar ClientMsg -> MVar ServerMsg -> Identifier -> Password -> Salt -> IO SRP6SharedSecret+clientAuthentication clientMsgVar serverMsgVar identifier password salt = do+ SendServerPublicKey serverPublicKey <- takeMVar serverMsgVar+ rng <- rngInit UserRNG+ (clientPublicKey, clientSecretKey) <-+ srp6ClientAgree identifier password group hash salt serverPublicKey rng+ putMVar clientMsgVar $ SendClientPublicKey clientPublicKey+ pure clientSecretKey++{-+The server then receives the client's public key, and generates a matching+shared secret key:+-}++serverAuthentication :: MVar ClientMsg -> SRP6ServerSession -> IO SRP6SharedSecret+serverAuthentication clientMsgVar session = do+ SendClientPublicKey clientPublicKey <- takeMVar clientMsgVar+ serverSecretKey <- srp6ServerSessionStep2 session group clientPublicKey+ pure serverSecretKey++{-+At this point, the client's and servers' secret key should be equal. In+practice, the secret keys should be confirmed to match by exchanging a hash+digest of relevant protocol values, susch as the secret key, public keys,+identifier, salt, and more. Calculation of this hash digest is not part of this+example. Instead, we will return the secret keys from the client and server so+that we can compare them, which obviously breaks security, but this should be+fine for our example.++We bring the client-side of the algorithm together by picking an identifier and+password, and calling the client-side functions in order.+-}++client :: MVar ClientMsg -> MVar ServerMsg -> IO SRP6SharedSecret+client clientMsgVar serverMsgVar = do+ let identifier = "alice"+ password = "Fee fi fo fum!"+ salt <- clientSignup clientMsgVar identifier password+ clientStartAuthentication clientMsgVar identifier+ clientAuthentication clientMsgVar serverMsgVar identifier password salt++{-+We bring the server-side of the algorithm together by initialising a session and+calling the server-side functions in order.+-}++server :: MVar ClientMsg -> MVar ServerMsg -> IO SRP6SharedSecret+server clientMsgVar serverMsgVar = do+ usersRef <- newIORef Map.empty+ serverSignup clientMsgVar usersRef+ session <- serverStartAuthentication clientMsgVar serverMsgVar usersRef+ serverAuthentication clientMsgVar session++{-+Using the @async@ package, we spin up a thread for the client and a thread for+the server. We wait for them to finish, and then compare the resulting secret+keys.+-}++main :: IO ()+main = do+ clientMsgVar <- newEmptyMVar+ serverMsgVar <- newEmptyMVar+ (clientSecretKey, serverSecretKey) <-+ concurrently (client clientMsgVar serverMsgVar) (server clientMsgVar serverMsgVar)+ let match = clientSecretKey == serverSecretKey+ if match then+ print @String "client secret == server secret"+ else+ error $+ printf "%s /= %s"+ (show clientSecretKey)+ (show serverSecretKey)++{-+All that is left to do is run the program.+-}+
+ test/Test/Botan/Low/TOTP.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.TOTP (tests) where++import Botan.Low.TOTP+import Data.ByteString+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.ByteString+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_totp" spec_totp+ pure $ testGroup "Test.Botan.Low.TOTP" [+ specs+ ]++key :: ByteString+key = "Fee fi fo fum"++timestep :: TOTPTimestep+timestep = 60++timestamp :: TOTPTimestamp+timestamp = 1698109812++-- TODO: More thorough testing+spec_totp :: Spec+spec_totp = testSuite totpHashes chars $ \ h -> do+ it "totpInit" $ do+ _ctx <- totpInit key h 6 timestep+ pass+ it "totpGenerate" $ do+ ctx <- totpInit key h 6 timestep+ _code <- totpGenerate ctx timestamp+ pass+ it "totpCheck" $ do+ ctx <- totpInit key h 6 timestep+ code <- totpGenerate ctx timestamp+ success <- totpCheck ctx code timestamp 0+ success `shouldBe` True
+ test/Test/Botan/Low/Utility.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.Utility (tests) where++import Botan.Low.Utility+import Data.ByteString as BS+import Data.Text+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_utility" spec_utility+ pure $ testGroup "Test.Botan.Low.Utility" [+ specs+ ]++message :: ByteString+message = "Fee fi fo fum!"++hexMessage :: Text+hexMessage = "46656520666920666F2066756D21"++base64Message :: Text+base64Message = "RmVlIGZpIGZvIGZ1bSE="++-- hexUpperMessage++spec_utility :: Spec+spec_utility = do+ it "constantTimeCompare" $ do+ equal <- constantTimeCompare "compare me" "compare me" (BS.length "compare me")+ equal `shouldBe` True+ pass+ -- TODO:+ -- it "scrubMem" $ do+ -- pass+ it "hexEncode" $ do+ hex <- hexEncode message HexUpperCase+ hex `shouldBe` hexMessage+ pass+ it "hexDecode" $ do+ msg <- hexDecode hexMessage+ msg `shouldBe` message+ pass+ it "base64Encode" $ do+ base64 <- base64Encode message+ base64 `shouldBe` base64Message+ pass+ it "base64Decode" $ do+ msg <- base64Decode base64Message+ msg `shouldBe` message+ pass
+ test/Test/Botan/Low/Version.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.Version (tests) where++import Botan.Low.Version+import Test.Tasty+import Test.Tasty.QuickCheck++tests :: TestTree+tests = testGroup "Test.Botan.Low.Version" [+ testProperty "prop_ffiSupportsItsOwnVersion" prop_ffiSupportsItsOwnVersion+ ]++prop_ffiSupportsItsOwnVersion :: Property+prop_ffiSupportsItsOwnVersion = once $ ioProperty $ do+ version <- botanFFIAPIVersion+ supports <- botanFFISupportsAPI version+ pure $+ counterexample ("version: " <> show version)+ supports
+ test/Test/Botan/Low/X509.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.X509 (tests) where++import Botan.Low.X509+import Data.ByteString+import Data.Word+import Paths_botan_low+import Test.Hspec+import Test.Tasty+import Test.Tasty.Hspec+import Test.Util.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_x509" spec_x509+ -- TODO: implement pending tests+ pure $ localOption TreatPendingAsSuccess+ $ testGroup "Test.Botan.Low.X509" [+ specs+ ]++-- NOTE: Taken from https://fm4dd.com/openssl/certexamples.shtm+testCert :: ByteString+testCert = "-----BEGIN CERTIFICATE-----\+\MIID2jCCA0MCAg39MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\+\A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\+\MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\+\YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\+\ODIyMDUyODAwWhcNMTcwODIxMDUyODAwWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\+\CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\+\ZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwvWITOLeyTbS1\+\Q/UacqeILIK16UHLvSymIlbbiT7mpD4SMwB343xpIlXN64fC0Y1ylT6LLeX4St7A\+\cJrGIV3AMmJcsDsNzgo577LqtNvnOkLH0GojisFEKQiREX6gOgq9tWSqwaENccTE\+\sAXuV6AQ1ST+G16s00iN92hjX9V/V66snRwTsJ/p4WRpLSdAj4272hiM19qIg9zr\+\h92e2rQy7E/UShW4gpOrhg2f6fcCBm+aXIga+qxaSLchcDUvPXrpIxTd/OWQ23Qh\+\vIEzkGbPlBA8J7Nw9KCyaxbYMBFb1i0lBjwKLjmcoihiI7PVthAOu/B71D2hKcFj\+\Kpfv4D1Uam/0VumKwhwuhZVNjLq1BR1FKRJ1CioLG4wCTr0LVgtvvUyhFrS+3PdU\+\R0T5HlAQWPMyQDHgCpbOHW0wc0hbuNeO/lS82LjieGNFxKmMBFF9lsN2zsA6Qw32\+\Xkb2/EFltXCtpuOwVztdk4MDrnaDXy9zMZuqFHpv5lWTbDVwDdyEQNclYlbAEbDe\+\vEQo/rAOZFl94Mu63rAgLiPeZN4IdS/48or5KaQaCOe0DuAb4GWNIQ42cYQ5TsEH\+\Wt+FIOAMSpf9hNPjDeu1uff40DOtsiyGeX9NViqKtttaHpvd7rb2zsasbcAGUl+f\+\NQJj4qImPSB9ThqZqPTukEcM/NtbeQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIAi\+\gU3My8kYYniDuKEXSJmbVB+K1upHxWDA8R6KMZGXfbe5BRd8s40cY6JBYL52Tgqd\+\l8z5Ek8dC4NNpfpcZc/teT1WqiO2wnpGHjgMDuDL1mxCZNL422jHpiPWkWp3AuDI\+\c7tL1QjbfAUHAQYwmHkWgPP+T2wAv0pOt36GgMCM\+\-----END CERTIFICATE-----\+\"++-- NOTE: Taken from https://fm4dd.com/openssl/certexamples.shtm+testCertFile :: FilePath+testCertFile = "test-data/4096b-rsa-example-cert.pem"++{-+Version: 1+Subject: C="JP",X520.State="Tokyo",O="Frank4DD",CN="www.example.com"+Issuer: C="JP",X520.State="Tokyo",X520.Locality="Chuo-ku",O="Frank4DD",OU="WebCert Support",CN="Frank4DD Web CA",PKCS9.EmailAddress="support@frank4dd.com"+Issued: 2012/08/22 05:28:00 UTC+Expires: 2017/08/21 05:28:00 UTC+Constraints:+ None+Signature algorithm: RSA/EMSA3(SHA-1)+Serial number: 0DFD+Public Key [RSA-4096]++-----BEGIN PUBLIC KEY-----+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsL1iEzi3sk20tUP1GnKn+iCyCtelBy70spiJW24k+5qQ+EjMAd+N8aSJVzeuHwtGNcpU+iy3l+ErewHCaxiFd+wDJiXLA7Dc4KOe+y6rTb5zpCx9BqI4rBRCkIkRF+oDoKvbVkqsGhDXHExLAF7leg+ENUk/hterNNIjfdoY1/Vf1eurJ0cE7Cf6eFkaS0nQI+Nu9oYjNfaiIPc64fdntq0+MuxP1EoVuIKTq4YNn+n3AgZvmlyIGvqsWki3IXA1Lz166SMU3fzlkNt0IbyBM5Bm+z5QQPCezcPSgsmsW2DARW9YtJQY8Ci45nKIoYiOz1bYQDrvwe9Q9oSnBYyqX7+A9+VGpv9FbpisIcLoWVTYy6tQUdRSkSdQoqCxuMAk69C1YLb71MoRa0vtz3VEdE+R5Q+EFjzMkAx4AqWzh1tMHNIW7jXjv5UvNi44nhjRcSpjARRfZbDds7AOkMN9l5G9vxB+ZbVwrabjsFc7XZODA652g18vczGbqhR6b+ZVk2w1cA3chEDXJWJWwBGw3rxEKP6w+DmRZfeDLut6wIC4j3mTeCHUv+PKK+SmkGgjntA7gG+BljSEONnGEOU7BB1rfhSDg+DEqX/YTT4w3rtbn3+NAzrbIshnl/TVYqirbbWh6b3e629s7GrG3ABlJfnzUCY+Ki+Jj0gfU4amaj07pBHDPzbW3kCAwEAAQ==+-----END PUBLIC KEY-----++-}++testCertHostname :: ByteString+testCertHostname = "www.example.com"++testCertValidTimestamp :: Word64+testCertValidTimestamp = 1420092000++spec_x509 :: Spec+spec_x509 = do+ it "x509CertLoad" $ do+ _cert <- x509CertLoad testCert+ pass+ it "x509CertLoadFile" $ do+ _cert <- x509CertLoadFile =<< getDataFileName testCertFile+ pass+ it "x509CertDup" $ do+ cert <- x509CertLoad testCert+ _dup <- x509CertDup cert+ -- TODO: parity check, eg: x509CertGetPublicKey cert == x509CertGetPublicKey dup+ pass+ it "x509CertGetTimeStarts" $ do+ cert <- x509CertLoad testCert+ _ts <- x509CertGetTimeStarts cert+ pass+ it "x509CertGetTimeExpires" $ do+ cert <- x509CertLoad testCert+ _te <- x509CertGetTimeExpires cert+ pass+ it "x509CertNotBefore" $ do+ cert <- x509CertLoad testCert+ _nb <- x509CertNotBefore cert+ pass+ it "x509CertNotAfter" $ do+ cert <- x509CertLoad testCert+ _na <- x509CertNotAfter cert+ pass+ it "x509CertGetPubKeyFingerprint" $ do+ cert <- x509CertLoad testCert+ _fp <- x509CertGetPubKeyFingerprint cert "SHA-256" -- TODO: HashName+ pass+ it "x509CertGetSerialNumber" $ do+ cert <- x509CertLoad testCert+ _sn <- x509CertGetSerialNumber cert+ pass+ it "x509CertGetAuthorityKeyId" $ do+ cert <- x509CertLoad testCert+ _akid <- x509CertGetAuthorityKeyId cert+ pass+ it "x509CertGetSubjectKeyId" $ do+ cert <- x509CertLoad testCert+ _sid <- x509CertGetSubjectKeyId cert+ pass+ it "x509CertGetPublicKeyBits" $ do+ cert <- x509CertLoad testCert+ _pkbits <- x509CertGetPublicKeyBits cert+ pass+ it "x509CertGetPublicKey" $ do+ cert <- x509CertLoad testCert+ _pk <- x509CertGetPublicKey cert+ pass+ it "x509CertGetIssuerDN" $ do+ cert <- x509CertLoad testCert+ _ <- x509CertGetIssuerDN cert "CN" 0 -- TODO: Distinguished names, etc+ pass+ it "x509CertGetSubjectDN" $ do+ cert <- x509CertLoad testCert+ _ <- x509CertGetSubjectDN cert "CN" 0 -- TODO: Distinguished names, etc+ pass+ it "x509CertToString" $ do+ _cert <- x509CertLoad testCert+ pass+ it "x509CertAllowedUsage" $ do+ cert <- x509CertLoad testCert+ _allowed <- x509CertAllowedUsage cert NoConstraints+ pass+ it "x509CertHostnameMatch" $ do+ cert <- x509CertLoad testCert+ _matched <- x509CertHostnameMatch cert testCertHostname+ pass+ it "x509CertVerify" $ do+ cert <- x509CertLoad testCert+ (_success, _status) <- x509CertVerify cert [] [] Nothing 0 testCertHostname testCertValidTimestamp+ pass+ it "x509CertValidationStatus" $ do+ _cert <- x509CertLoad testCert+ _ <- x509CertValidationStatus 0+ pending+ -- NOTE: May need to generate a proper test cert with CA and CRL+ it "x509CRLLoad" $ do+ _cert <- x509CertLoad testCert+ -- x509CRLLoad cert+ pending+ it "x509CRLLoadFile" $ do+ _cert <- x509CertLoad testCert+ -- x509CRLLoadFile cert+ pending+ it "x509IsRevoked" $ do+ _cert <- x509CertLoad testCert+ -- x509IsRevoked cert+ pending+ it "x509CertVerifyWithCLR" $ do+ _cert <- x509CertLoad testCert+ -- x509CertVerifyWithCLR cert+ pending+
+ test/Test/Botan/Low/ZFEC.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Botan.Low.ZFEC (tests) where++import Botan.Low.ZFEC+import Data.ByteString (ByteString)+import Test.Hspec+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.Hspec++tests :: IO TestTree+tests = do+ specs <- testSpec "spec_zfec" spec_zfec+ pure $ testGroup "Test.Botan.Low.ZFEC" [+ specs+ ]+k :: Int+k = 5++n :: Int+n = 7++message :: ByteString+message = "Fee fi fo fum! I smell the blood of an Englishman!"++spec_zfec :: Spec+spec_zfec = do+ describe "zfecEncode" $ do+ it "encodes a message into shares" $ do+ shares <- zfecEncode k n message+ length shares `shouldBe` n+ describe "zfecDecode" $ do+ it "recovers a message from enough shares" $ do+ shares <- zfecEncode k n message+ someShares <- take k <$> generate (shuffle shares)+ recoveredMessage <- zfecDecode k n someShares+ recoveredMessage `shouldBe` message
− test/Test/Prelude.hs
@@ -1,50 +0,0 @@-module Test.Prelude-( module Prelude-, module Test.Hspec-, module Test.QuickCheck-, module Control.Monad-, module Data.ByteString-, chars-, splitBlocks-, testSuite-, ftestSuite-, pass-, anyBotanException-) where--import Prelude--import Test.Hspec-import Test.QuickCheck--import Control.Monad--import Data.ByteString (ByteString)-import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Char8 as Char8--import Botan.Low.Error--chars :: ByteString -> [Char]-chars = Char8.unpack--splitBlocks :: Int -> ByteString -> [ByteString]-splitBlocks blockSize = go where- go bytes = case ByteString.splitAt blockSize bytes of- (block,"") -> [block]- (block,rest) -> block : go rest--testSuite :: [t] -> (t -> String) -> (t -> SpecWith a) -> SpecWith a-testSuite tests testName runTest = forM_ tests $ \ test -> describe (testName test) (runTest test)--ftestSuite :: [t] -> (t -> String) -> (t -> SpecWith a) -> SpecWith a-ftestSuite tests testName runTest = focus (testSuite tests testName runTest)---- An alternative to void?--- _ <- performSomeAction--- pass-pass :: (Monad m) => m ()-pass = return ()--anyBotanException :: Selector SomeBotanException-anyBotanException = const True
+ test/Test/Util/ByteString.hs view
@@ -0,0 +1,19 @@+module Test.Util.ByteString (+ chars+ , splitBlocks+ ) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC++chars :: ByteString -> [Char]+chars = BSC.unpack++splitBlocks :: Int -> ByteString -> [ByteString]+splitBlocks blockSize = go+ where+ go bytes = case BS.splitAt blockSize bytes of+ (block,rest)+ | BS.null rest -> [block]+ | otherwise -> block : go rest
+ test/Test/Util/Hspec.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Util.Hspec (+ testSuite+ , pass+ ) where++import Control.Monad+import Test.Hspec++testSuite :: [t] -> (t -> String) -> (t -> SpecWith a) -> SpecWith a+testSuite tests testName runTest = forM_ tests $ \ test -> describe (testName test) (runTest test)++-- An alternative to void?+-- _ <- performSomeAction+-- pass+pass :: (Monad m) => m ()+pass = return ()